initial commit: cloned OGN iauth
authorpk910 <philipp@zoelle1.de>
Wed, 3 Aug 2011 12:37:57 +0000 (14:37 +0200)
committerpk910 <philipp@zoelle1.de>
Wed, 3 Aug 2011 12:37:57 +0000 (14:37 +0200)
Makefile [new file with mode: 0644]
iauth.c [new file with mode: 0644]
iauth.h [new file with mode: 0644]
iauth.php [new file with mode: 0644]
iauth_cmd.c [new file with mode: 0644]
iauth_io.c [new file with mode: 0644]
iauth_query.c [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..7f61802
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+CFLAGS=-O0 -g -Wall -Wshadow -Werror
+
+all:
+       gcc -o iauth -O0 -g -Wall iauth.c iauth_io.c iauth_query.c iauth_cmd.c -Wshadow -Werror
+
+library:
+       gcc -o libiauth.so
+
+binary:
diff --git a/iauth.c b/iauth.c
new file mode 100644 (file)
index 0000000..6ebb8d0
--- /dev/null
+++ b/iauth.c
@@ -0,0 +1,159 @@
+/*
+ * Written by David Herrmann.
+ * Dedicated to the Public Domain.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include "iauth.h"
+
+static unsigned int running = 1;
+
+static void iauth_signal(int sig) {
+    iauth_flog(IAUTH_INFO, "Caught signal %d, ignoring...", sig);
+}
+
+/* Parses one single input line. */
+void iauth_parse(char *line) {
+    unsigned int i, found;
+    signed int id;
+
+    /* The line has to look like:
+     * <id> X <several> <arguments>
+     */
+
+    found = 0;
+    for(i = 0; line[i]; ++i) {
+        if(line[i] == ' ') {
+            found = 1;
+            break;
+        }
+    }
+
+    /* Ignore invalid lines. */
+    if(!found) {
+        iauth_flog(IAUTH_WARNING, "Received invalid line; no subcommand specified.");
+        return;
+    }
+
+    /* Get the id. */
+    id = atoi(line);
+
+    /* Ignore invalid subcommands. */
+    if(!line[++i] || line[i] == ' ' || (line[i + 1] != ' ' && line[i + 1])) {
+        iauth_flog(IAUTH_WARNING, "Received invalid line; invalid subcommand.");
+        return;
+    }
+
+    /* Call the subcommand handler. */
+    switch(line[i]) {
+        case 'C':
+            /* Client Introduction: <id> C <remoteip> <remoteport> <localip> <localport> */
+            if(id >= 0 && id < iauth_clients_size && line[++i]) iauth_cmd_C(id, &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid C line.");
+            break;
+        case 'D':
+            /* Client Disconnect: <id> D */
+            if(id >= 0 && id < iauth_clients_size) {
+                if(iauth_clients[id].id == id) iauth_cmd_D(&iauth_clients[id]);
+                else /* Ignore all other disconnects. */ ;
+            }
+            else iauth_flog(IAUTH_WARNING, "Received invalid D line.");
+            break;
+        case 'L':
+            /* Login On Connect: <id> L <account>[:<accountstamp>][ <fakehost>] */
+            if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_L(&iauth_clients[id], &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid L line.");
+            break;
+        case 'H':
+            /* Hurry Up: <id> H <class> */
+            if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_H(&iauth_clients[id], &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid H line.");
+            break;
+        case 'M':
+            /* Server Name and Capacity: <id> M <servername> <capacity> */
+            if(id == -1 && line[++i]) iauth_cmd_M(&line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid M line.");
+            break;
+        case 'N':
+            /* Hostname Received: <id> N <hostname> */
+            if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_N(&iauth_clients[id], &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid N line.");
+            break;
+        case 'd':
+            /* Hostname Timeout: <id> d */
+            if(id >= 0 && id < iauth_clients_size && iauth_clients[id].id == id) iauth_cmd_d(&iauth_clients[id]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid d line.");
+            break;
+        case 'P':
+            /* Client Password: <id> P :<password ...> */
+            if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_P(&iauth_clients[id], &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid P line.");
+            break;
+        case 'U':
+            /* Client Username: <id> U <username> <hostname> <servername> :<userinfo ...> */
+            if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_U(&iauth_clients[id], &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid U line.");
+            break;
+        case 'u':
+            /* Client Username: <id> u <username> */
+            if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_u(&iauth_clients[id], &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid u line.");
+            break;
+        case 'n':
+            /* Client Nickname: <id> n <nickname> */
+            if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_n(&iauth_clients[id], &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid n line.");
+            break;
+        case 'T':
+            /* Client Registered: <id> T */
+            if(id >= 0 && id < iauth_clients_size && iauth_clients[id].id == id) iauth_cmd_T(&iauth_clients[id]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid T line.");
+            break;
+        case 'E':
+            /* Error: <id> E <type> :<additional text> */
+            if(line[++i]) iauth_cmd_E(id, &line[++i]);
+            else iauth_flog(IAUTH_WARNING, "Received invalid E line.");
+            break;
+        default:
+            /* Invalid subcommand; ignore. */
+            iauth_flog(IAUTH_WARNING, "Received invalid subcommand.");
+            return;
+    }
+
+    /* Subcommand called, nothing to do; return and await next line. */
+    return;
+}
+
+signed int main(signed int argc, char *argv[]) {
+    char *line;
+
+    signal(SIGPIPE, SIG_IGN);
+    signal(SIGHUP, SIG_IGN);
+    signal(SIGINT, iauth_signal);
+    signal(SIGQUIT, iauth_signal);
+    signal(SIGTERM, iauth_signal);
+
+    if(argc > 1) iauth_logfile = argv[1];
+    if(argc > 2) iauth_scriptfile = argv[2];
+    if(argc > 3) iauth_debug = 1;
+
+    iauth_flog(IAUTH_INFO, "Starting IAuth");
+
+    iauth_query_version("OGN iauth");
+    iauth_query_policy("ARTU");
+    iauth_stats_report();
+
+    while(running && (line = iauth_read())) {
+        iauth_parse(line);
+    }
+
+    iauth_flog(IAUTH_INFO, "Stopping IAuth");
+
+    return EXIT_SUCCESS;
+}
diff --git a/iauth.h b/iauth.h
new file mode 100644 (file)
index 0000000..2e0abf6
--- /dev/null
+++ b/iauth.h
@@ -0,0 +1,182 @@
+/*
+ * Written by David Herrmann.
+ * Dedicated to the Public Domain.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+
+/* Global configuration.
+ * It is not recommended to change these values. They are set to
+ * an especially high value to support even the weirdest environments.
+ */
+/* Maximal length of a line. */
+#define IAUTH_LINE 4096
+/* Maximal capacity. */
+#define IAUTH_CAPMAX 1000000
+/* Maximum length of accounts/classes/fakehosts etc.. */
+#define IAUTH_DATALEN 256
+
+/* 1 if debug mode is enabled, otherwise 0. */
+extern unsigned int iauth_debug;
+
+/* Log functions.
+ * These functions write status data into a log file.
+ * They all do the same but take either a formatted argument
+ * or a va_list buffer.
+ * Error functions log the message and terminate the application,
+ * log functions only log the message.
+ */
+#define IAUTH_FATAL 0
+#define IAUTH_WARNING 1
+#define IAUTH_INFO 2
+#define IAUTH_DEBUG 3
+extern const char *iauth_logfile;
+extern void iauth_ferror(const char *format, ...);
+extern void iauth_verror(const char *format, va_list list);
+extern void iauth_flog(unsigned int type, const char *format, ...);
+extern void iauth_vlog(unsigned int type, const char *format, va_list list);
+extern void iauth_eflog(const char *format, ...);
+extern void iauth_evlog(const char *format, va_list list);
+
+/* IO functions.
+ * These functions either write a line to the IAuth or read a
+ * single line from the IAuth.
+ */
+extern char *iauth_read();
+extern void iauth_fsend(const char *format, ...);
+extern void iauth_vsend(const char *format, va_list list);
+
+/* Allocates/frees memory.
+ * Aborts with an appropriate message if the allocation fails.
+ */
+static inline void *iauth_malloc(size_t size) {
+    void *mem;
+    if(!(mem = malloc(size))) iauth_eflog("Memory allocation failed.");
+    memset(mem, 0, size);
+    return mem;
+}
+static inline char *iauth_strdup(const char *str) {
+    char *mem;
+    if(!(mem = strdup(str))) iauth_eflog("strdup() failed.");
+    return mem;
+}
+#define iauth_free(x) ((void)((x)?free(x):0))
+
+/* User management.
+ * Adds or removes a user.
+ */
+struct iauth_client {
+    signed int id;
+    char *ip;
+    unsigned short port;
+    char *lo_ip;
+    unsigned short lo_port;
+    char *host;
+    char *c_host;
+    char *c_serv;
+    char *nick;
+    char *username;
+    char *realname;
+    char *account;
+    char *fakehost;
+    char *cclass;
+    char *password;
+    char *ident;
+    unsigned int state_r : 1; /* Is in "registering" state. */
+    unsigned int state_h : 1; /* Is in "hurry" state. */
+};
+#define iauth_set(x, y) (iauth_free(x), (x = y))
+extern struct iauth_client *iauth_clients;
+extern unsigned int iauth_clients_size;
+extern void iauth_setcap(unsigned int cap);
+extern void iauth_addid(signed int id);
+extern void iauth_delid(signed int id);
+
+/* Request handler.
+ * The real requests are outsourced to a scriptfile.
+ * The iauth process spawns the scriptfile, passes the
+ * data as arguments and exspects the process to return
+ * the result on stdout.
+ * The scriptfile is spawned for every request.
+ */
+extern char *iauth_scriptfile;
+struct iauth_result {
+    char cclass[IAUTH_DATALEN + 1];
+    char ident[IAUTH_DATALEN + 1];
+    char host[IAUTH_DATALEN + 1];
+    char ip[IAUTH_DATALEN + 1];
+    char modes[IAUTH_DATALEN + 1];
+};
+extern const struct iauth_result *iauth_query(struct iauth_client *cli);
+extern char iauth_servname[IAUTH_DATALEN + 1];
+
+/* Sends a specific request to the ircd.
+ * This is a less generic but easier to use interface
+ * for the iauth_[vf]send() commands. This interface also
+ * correctly sends statistics.
+ */
+
+/* Operator Notification: > :<message text> */
+extern void iauth_query_fnotify(const char *format, ...);
+extern void iauth_query_vnotify(const char *format, va_list list);
+/* Set Debug Level: G <level> */
+extern void iauth_query_debug(unsigned int debuglevel);
+/* Set Policy Options: O <options> */
+extern void iauth_query_policy(const char *policy);
+/* iauth Program Version: V :<version string> */
+extern void iauth_query_version(const char *version);
+/* Start of new configuration: a */
+extern void iauth_query_newconf();
+/* Configuration Information: A <hosts?> <module> :<options> */
+extern void iauth_query_config(const char *hosts, const char *module, const char *value);
+/* Start of new statistics: s */
+extern void iauth_query_newstats();
+/* Statistics Information: S <module> :<module information> */
+extern void iauth_query_stats(const char *module, const char *value);
+/* Forced Username: o <id> <remoteip> <remoteport> <username> */
+extern void iauth_query_set_username(signed int id, const char *username);
+/* Trusted Username: U <id> <remoteip> <remoteport> <username> */
+extern void iauth_query_trust_username(signed int id, const char *username);
+/* Untrusted Username: u <id> <remoteip> <remoteport> <username> */
+extern void iauth_query_distrust_username(signed int id, const char *username);
+/* Client Hostname: N <id> <remoteip> <remoteport> <hostname> */
+extern void iauth_query_sethost(signed int id, const char *hostname);
+/* Client IP Address: I <id> <currentip> <remoteport> <newip> */
+extern void iauth_query_setip(signed int id, const char *ip);
+/* Adjust User Mode: M <id> <remoteip> <remoteport> +<mode changes> */
+extern void iauth_query_setmodes(signed int id, const char *modes);
+/* Challenge User: C <id> <remoteip> <remoteport> :<challenge string> */
+extern void iauth_query_challenge(signed int id, const char *challenge);
+/* Quietly Kill Client: k <id> <remoteip> <remoteport> :<reason> */
+extern void iauth_query_reject(signed int id, const char *reason);
+/* Kill Client: K <id> <remoteip> <remoteport> :<reason> */
+extern void iauth_query_kill(signed int id, const char *reason);
+/* Done Checking: D <id> <remoteip> <remoteport> [class] */
+extern void iauth_query_assign(signed int id, const char *cclass);
+/* Registered User: R <id> <remoteip> <remoteport> <account> [class] */
+extern void iauth_query_register(signed int id, const char *account, const char *cclass);
+
+/* Subcommand handlers. */
+extern void iauth_cmd_C(signed int id, char *arg);
+extern void iauth_cmd_D(struct iauth_client *client);
+extern void iauth_cmd_L(struct iauth_client *client, char *arg);
+extern void iauth_cmd_H(struct iauth_client *client, char *arg);
+extern void iauth_cmd_M(char *arg);
+extern void iauth_cmd_N(struct iauth_client *client, char *arg);
+extern void iauth_cmd_d(struct iauth_client *client);
+extern void iauth_cmd_P(struct iauth_client *client, char *arg);
+extern void iauth_cmd_U(struct iauth_client *client, char *arg);
+extern void iauth_cmd_u(struct iauth_client *client, char *arg);
+extern void iauth_cmd_n(struct iauth_client *client, char *arg);
+extern void iauth_cmd_T(struct iauth_client *client);
+extern void iauth_cmd_E(signed int id, char *arg);
+
+/* Statistics */
+extern void iauth_stats_report();
+extern void iauth_stats_loc_allow();
+extern void iauth_stats_loc_deny();
+extern void iauth_stats_def_allow();
+extern void iauth_stats_def_deny();
+
diff --git a/iauth.php b/iauth.php
new file mode 100644 (file)
index 0000000..267e871
--- /dev/null
+++ b/iauth.php
@@ -0,0 +1,135 @@
+#!/usr/bin/php
+<?php
+/* Written by David Herrmann.
+ * Dedicated to the Public Domain.
+ */
+/* PHP IAuth verifier.
+ * The IAuth collects all information from the ircd and when it thinks it has a complete
+ * set of information, it starts this script with all information as parameters. This
+ * script has to check the database for a dataset and return the values it wants to change
+ * or return nothing if it wants to reject the client.
+ *
+ * The data from the IAuth is passed in the array $argv. If a value is not available, it is
+ * an empty string. If a value is available it must be a string between 1 and IAUTH_DATALEN
+ * characters, whereas IAUTH_DATALEN is defined in iauth.h.
+ *  - Name of the script:
+ *     $argv[0] = ./iauth.php
+ *  - The IP of the remote socket endpoint:
+ *     $argv[1] = 85.214.49.253
+ *  - The port of the remote socket endpoint:
+ *     $argv[2] = 17655
+ *  - The ip of the local socket endpoint:
+ *     $argv[3] = 127.0.0.1
+ *  - The port of the local socket endpoint:
+ *     $argv[4] = 6667
+ *  - The resolved hostname of the remote socket:
+ *     $argv[5] = p3EE37393.dip.t-dialin.net
+ *  - The hostname the user passed to USER:
+ *     $argv[6] = localhost
+ *  - The servername the user passed to USER:
+ *     $argv[7] = irc.ogn.net
+ *  - The nick which the user passed:
+ *     $argv[8] = some_weird_nick
+ *  - The username the user passed to USER:
+ *     $argv[9] = username
+ *  - The realname the user passed to USER:
+ *     $argv[10] = realname
+ *  - The account[:timestamp] which was proofed by LOC: (The :timestamp is optional)
+ *     $argv[11] = some_account:124206424
+ *  - The fakehost which was set by LOC:
+ *     $argv[12] = cool.1337.fakehost
+ *  - The class that the server would assign to the user if iauth would not be there:
+ *     $argv[13] = some_server_class
+ *  - The last PASS line the user sent:
+ *     $argv[14] = some_password
+ *  - The ident we got from the user's ident server:
+ *     $argv[15] = ident
+ *  - The name of the server we are connected to:
+ *     $argv[16] = devnull.xy.net
+ *
+ * The response of the script is sent to STDOUT. If the script wants to reject the request, it can
+ * simply exit without sending anything.
+ * If you want to accept the client, you have to pass several parameters to STDOUT. Each parameter
+ * is separated by a space. If you want to skip a parameter, simply put "$" in there.
+ * Every value which is not "$" is forced on the user before he gets assigned to a class.
+ * Each value is limited again to IAUTH_DATALEN, however, the ircd itself may limit the data again,
+ * therefore, it is recommended to use short values. '\0' characters are not allowed in a reply and
+ * the IAuth parser will reject the query.
+ * The values are:
+ *  - The class which is assigned to the user:
+ *     echo "some_class ";
+ *  - The ident which should be forced on the user:
+ *     echo "FORCEIDENT ";
+ *  - The host which should be forced on the user:
+ *     echo "forced.host.on.user ";
+ *  - The ip which should be forced on the user:
+ *     echo "127.244.12.110 ";
+ *  - A mode striing which is set on the user. This can include fakehosts/accounts/operators/etc.
+ *     echo "+wogsfr 131071 fake.host.net account:124653295"
+ * The last parameter "mode" can have as many spaces as you want.
+ */
+
+/* These constants are defined to access $argv more easily. */
+define("ARG_REMOTEIP", $argv[1]);
+define("ARG_REMOTEPORT", $argv[2]);
+define("ARG_LOCALIP", $argv[3]);
+define("ARG_LOCALPORT", $argv[4]);
+define("ARG_HOSTNAME", $argv[5]);
+define("ARG_USER_HOST", $argv[6]);
+define("ARG_USER_SERV", $argv[7]);
+define("ARG_NICK", $argv[8]);
+define("ARG_USERNAME", $argv[9]);
+define("ARG_REALNAME", $argv[10]);
+define("ARG_TS_ACCOUNT", $argv[11]);
+define("ARG_ACCOUNT", preg_replace('/^(.*?)(:\d+)?$/', '$1', $argv[11]));
+define("ARG_FAKEHOST", $argv[12]);
+define("ARG_CLASS", $argv[13]);
+define("ARG_PASS", $argv[14]);
+define("ARG_IDENT", $argv[15]);
+define("ARG_SERVER", $argv[16]);
+
+/* This function can be used to return a result. */
+function iauth_return($class = NULL, $ident = NULL, $host = NULL, $ip = NULL, $mode = NULL) {
+    $class = trim($class);
+    $ident = trim($ident);
+    $host = trim($host);
+    $ip = trim((substr($ip, 0, 1) == ":") ? "0".$ip : $ip);
+    $mode = trim($mode);
+    if($class === NULL || strlen($class) == 0) $class = "$";
+    if($ident === NULL || strlen($ident) == 0) $ident = "$";
+    if($host === NULL || strlen($host) == 0) $host = "$";
+    if($ip === NULL || strlen($ip) == 0) $ip = "$";
+    if($mode === NULL || strlen($mode) == 0) $mode = "$";
+    echo "$class $ident $host $ip $mode";
+    exit(0);
+}
+
+/* This rejects the client. */
+function iauth_reject() {
+    exit(0);
+}
+
+/* Validate the input now and return the right result.
+ * REMEMBER: SOME VALUES MIGHT BE AN EMPTY STRING AND NOT SET!
+ */
+
+
+/****************************************************/
+/****************************************************/
+/* Following three example ways to handle a client. */
+/****************************************************/
+/****************************************************/
+
+/* Simply allow the client to connect the normal way. */
+/* iauth_return(); */
+
+/* Or as an example return only a class and a mode change. */
+/* iauth_return("class", NULL, NULL, NULL, "+rf account:14314789 fake.host.net"); */
+
+/* Or reject the client. */
+/* iauth_reject(); */
+
+/* our real implementation */
+iauth_return();
+
+?>
diff --git a/iauth_cmd.c b/iauth_cmd.c
new file mode 100644 (file)
index 0000000..5480c38
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * Written by David Herrmann.
+ * Dedicated to the Public Domain.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "iauth.h"
+
+char iauth_servname[IAUTH_DATALEN + 1];
+
+static char *iauth_next_data(char **arg) {
+    char *sep, *ret;
+    unsigned int len;
+
+    while(**arg == ' ') ++*arg;
+    if(!**arg) return NULL;
+    if(**arg == ':' || !(sep = strchr(*arg, ' '))) {
+        if(**arg == ':') ++*arg;
+        len = strlen(*arg);
+        ret = iauth_malloc(len + 1);
+        strcpy(ret, *arg);
+        *arg += len;
+        if(len > IAUTH_DATALEN) ret[IAUTH_DATALEN] = 0;
+        else ret[len] = 0;
+        return ret;
+    }
+    else {
+        *sep = 0;
+        len = strlen(*arg);
+        ret = iauth_malloc(len + 1);
+        strcpy(ret, *arg);
+        if(len > IAUTH_DATALEN) ret[IAUTH_DATALEN] = 0;
+        else ret[len] = 0;
+        *sep = ' ';
+        *arg = sep + 1;
+        return ret;
+    }
+}
+
+/* Client Introduction: <id> C <remoteip> <remoteport> <localip> <localport> */
+void iauth_cmd_C(signed int id, char *arg) {
+    char *str;
+    struct iauth_client *cli;
+
+    iauth_delid(id);
+    iauth_addid(id);
+    cli = &iauth_clients[id];
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(cli->ip, str);
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    cli->port = atoi(str);
+    iauth_free(str);
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(cli->lo_ip, str);
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    cli->lo_port = atoi(str);
+    iauth_free(str);
+
+    iauth_flog(IAUTH_INFO, "New client (%d) from '%s':%hu to '%s':%hu.", id, cli->ip, cli->port, cli->lo_ip, cli->lo_port);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid C line.");
+    iauth_delid(id);
+    return;
+}
+
+/* Client Disconnect: <id> D */
+void iauth_cmd_D(struct iauth_client *client) {
+    iauth_delid(client->id);
+}
+
+/* Login On Connect: <id> L <account>[:<accountstamp>][ <fakehost>] */
+void iauth_cmd_L(struct iauth_client *client, char *arg) {
+    char *str;
+    const struct iauth_result *res;
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->account, str);
+
+    str = iauth_next_data(&arg);
+    if(str) iauth_set(client->fakehost, str);
+
+    /* Query database for account query. */
+    res = iauth_query(client);
+    if(!res) {
+        iauth_query_reject(client->id, "Access denied.");
+        iauth_stats_loc_deny();
+    }
+    else {
+        if(*res->ident) iauth_query_set_username(client->id, res->ident);
+        if(*res->host) iauth_query_sethost(client->id, res->host);
+        if(*res->ip) iauth_query_setip(client->id, res->ip);
+        if(*res->modes) iauth_query_setmodes(client->id, res->modes);
+        iauth_query_assign(client->id, (*res->cclass)?res->cclass:NULL);
+        iauth_stats_loc_allow();
+    }
+
+    iauth_delid(client->id);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid L line.");
+    return;
+}
+
+/* Hurry Up: <id> H <class> */
+void iauth_cmd_H(struct iauth_client *client, char *arg) {
+    char *str;
+    const struct iauth_result *res;
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->cclass, str);
+    client->state_h = 1;
+
+    /* Query database for account query. */
+    res = iauth_query(client);
+    if(!res) {
+        iauth_query_reject(client->id, "Access denied.");
+        iauth_stats_def_deny();
+    }
+    else {
+        if(*res->ident) iauth_query_set_username(client->id, res->ident);
+        if(*res->host) iauth_query_sethost(client->id, res->host);
+        if(*res->ip) iauth_query_setip(client->id, res->ip);
+        if(*res->modes) iauth_query_setmodes(client->id, res->modes);
+        iauth_query_assign(client->id, (*res->cclass)?res->cclass:NULL);
+        iauth_stats_def_allow();
+    }
+    iauth_delid(client->id);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid H line.");
+    return;
+}
+
+/* Server Name and Capacity: <id> M <servername> <capacity> */
+void iauth_cmd_M(char *arg) {
+    char *server = NULL, *str = NULL;
+    unsigned int cap;
+
+    server = iauth_next_data(&arg);
+    if(!server) goto parse_error;
+
+    str = iauth_next_data(&arg);
+    if(!str || !*str || (cap = atoi(str)) == 0) goto parse_error;
+    iauth_setcap(cap);
+    iauth_flog(IAUTH_INFO, "Setting server (%s) capacity to: %u.", server, cap);
+    strcpy(iauth_servname, server);
+    iauth_free(str);
+    iauth_free(server);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid M line.");
+    iauth_free(server);
+    iauth_free(str);
+    return;
+}
+
+/* Hostname Received: <id> N <hostname> */
+void iauth_cmd_N(struct iauth_client *client, char *arg) {
+    char *str;
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->host, str);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid N line.");
+    return;
+}
+
+/* Hostname Timeout: <id> d */
+void iauth_cmd_d(struct iauth_client *client) {
+    char *str;
+
+    str = iauth_malloc(1);
+    iauth_set(client->host, str);
+
+    return;
+}
+
+/* Client Password: <id> P :<password ...> */
+void iauth_cmd_P(struct iauth_client *client, char *arg) {
+    char *str;
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->password, str);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid P line.");
+    return;
+}
+
+/* Client Username: <id> U <username> <hostname> <servername> :<userinfo ...> */
+void iauth_cmd_U(struct iauth_client *client, char *arg) {
+    char *str;
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->username, str);
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->c_host, str);
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->c_serv, str);
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->realname, str);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid U line.");
+    return;
+}
+
+/* Client Username: <id> u <username> */
+void iauth_cmd_u(struct iauth_client *client, char *arg) {
+    char *str;
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->ident, str);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid u line.");
+    return;
+}
+
+/* Client Nickname: <id> n <nickname> */
+void iauth_cmd_n(struct iauth_client *client, char *arg) {
+    char *str;
+
+    str = iauth_next_data(&arg);
+    if(!str) goto parse_error;
+    iauth_set(client->nick, str);
+
+    return;
+    parse_error:
+    iauth_flog(IAUTH_WARNING, "Parse error: Invalid n line.");
+    return;
+}
+
+/* Client Registered: <id> T */
+void iauth_cmd_T(struct iauth_client *client) {
+    iauth_flog(IAUTH_INFO, "Client %d was registered without IAuth answer.", client->id);
+    iauth_delid(client->id);
+}
+
+/* Error: <id> E <type> :<additional text> */
+void iauth_cmd_E(signed int id, char *arg) {
+    iauth_flog(IAUTH_WARNING, "Received IRCd error: %s", arg);
+}
+
diff --git a/iauth_io.c b/iauth_io.c
new file mode 100644 (file)
index 0000000..995d01b
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Written by David Herrmann.
+ * Dedicated to the Public Domain.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+#include <unistd.h>
+
+#include "iauth.h"
+
+/* /path/to/logfile.log or NULL. */
+const char *iauth_logfile = NULL;
+/* Current logfile or NULL. */
+FILE *logfile = NULL;
+/* 1 if debug mode is enabled, otherwise 0. */
+unsigned int iauth_debug = 0;
+
+static void iauth_error(const char *msg, size_t len) {
+    fwrite(msg, 1, len, stderr);
+    fwrite("\n", 1, 1, stderr);
+    /*abort();*/
+    exit(1);
+}
+
+void iauth_ferror(const char *format, ...) {
+    va_list arg;
+
+    va_start(arg, format);
+    iauth_verror(format, arg);
+    va_end(arg);
+}
+
+void iauth_verror(const char *format, va_list list) {
+    char buffer[IAUTH_LINE + 1];
+    size_t len;
+
+    memset(buffer, 0, IAUTH_LINE + 1);
+    len = vsnprintf(buffer, IAUTH_LINE, format, list);
+    if(!len) len = snprintf(buffer, IAUTH_LINE, "<no error message specified>");
+    iauth_error(buffer, len);
+}
+
+/* Checks whether we need to reopen the logfile.
+ * Aborts the application if the logfile cannot be
+ * opened.
+ */
+static void iauth_log_reopen() {
+    if(iauth_logfile) {
+        if(logfile) fclose(logfile);
+        logfile = fopen(iauth_logfile, "a");
+        if(!logfile) iauth_ferror("Logfile '%s' could not be opened in mode 'a'; errno = '%d'.", iauth_logfile, errno);
+        iauth_logfile = NULL;
+    }
+    else if(!logfile) iauth_ferror("No logfile specified.");
+}
+
+static void iauth_log(unsigned int type, const char *msg, size_t len) {
+    struct tm *tstamp;
+    time_t curtime;
+    char buffer[IAUTH_LINE + 1];
+    size_t len2;
+
+    iauth_log_reopen();
+
+    curtime = time(NULL);
+    tstamp = localtime(&curtime);
+    memset(buffer, 0, IAUTH_LINE + 1);
+    len2 = snprintf(buffer, IAUTH_LINE + 1, "[%02d.%02d.%d %02d:%02d:%02d]", tstamp->tm_mday, tstamp->tm_mon + 1, tstamp->tm_year + 1900, 
+                                                                             tstamp->tm_hour, tstamp->tm_min, tstamp->tm_sec);
+    len2 += snprintf(&buffer[len2], IAUTH_LINE + 1 - len2, " (%s): ", (type == IAUTH_FATAL)?"FATAL":(type == IAUTH_WARNING)?"WARNING":
+                                                                      (type == IAUTH_INFO)?"INFO":(type == IAUTH_DEBUG)?"DEBUG":"UNKNOWN");
+
+    if(fwrite(buffer, 1, len2, logfile) != len2) iauth_ferror("Write operation on logfile failed; errno = '%d'.", errno);
+    if(fwrite(msg, 1, len, logfile) != len) iauth_ferror("Write operation on logfile failed; errno = '%d'.", errno);
+    fwrite("\n", 1, 1, logfile);
+    fflush(logfile);
+}
+
+void iauth_flog(unsigned int type, const char *format, ...) {
+    va_list arg;
+
+    va_start(arg, format);
+    iauth_vlog(type, format, arg);
+    va_end(arg);
+}
+
+void iauth_vlog(unsigned int type, const char *format, va_list list) {
+    char buffer[IAUTH_LINE + 1];
+    size_t len;
+
+    memset(buffer, 0, IAUTH_LINE + 1);
+    len = vsnprintf(buffer, IAUTH_LINE, format, list);
+    if(!len) len = snprintf(buffer, IAUTH_LINE, "<no message specified>");
+    iauth_log(type, buffer, len);
+}
+
+void iauth_eflog(const char *format, ...) {
+    va_list arg;
+
+    va_start(arg, format);
+    iauth_evlog(format, arg);
+    va_end(arg);
+}
+
+void iauth_evlog(const char *format, va_list list) {
+    char buffer[IAUTH_LINE + 1];
+    size_t len;
+
+    memset(buffer, 0, IAUTH_LINE + 1);
+    len = vsnprintf(buffer, IAUTH_LINE, format, list);
+    if(!len) len = snprintf(buffer, IAUTH_LINE, "<no message specified>");
+    iauth_log(IAUTH_FATAL, buffer, len);
+    iauth_error(buffer, len);
+}
+
+char *iauth_read() {
+    static char buffer[IAUTH_LINE + 1];
+    unsigned int i, ignore = 0;
+
+    next_line:
+
+    if(!fgets(buffer, IAUTH_LINE, stdin)) {
+        if(feof(stdin)) return NULL;
+        iauth_eflog("Reading on stdin failed; errno = '%d'.", errno);
+    }
+    buffer[IAUTH_LINE] = 0;
+
+    for(i = 0; i < IAUTH_LINE; ++i) {
+        if(buffer[i] == '\n') {
+            if(ignore) {
+                ignore = 0;
+                goto next_line;
+            }
+            if(i == 0) goto next_line;
+            if(buffer[i - 1] == '\r') {
+                if(i == 1) goto next_line;
+                buffer[i - 1] = 0;
+            }
+            else buffer[i] = 0;
+            if(iauth_debug) {
+                iauth_flog(IAUTH_DEBUG, "IN -> %s", buffer);
+            }
+            return buffer;
+        }
+    }
+
+    /* Too long message. Discard it! */
+    iauth_flog(IAUTH_WARNING, "Message exceeded maximum length of '%d' bytes.", IAUTH_LINE);
+    ignore = 1;
+    goto next_line;
+}
+
+void iauth_fsend(const char *format, ...) {
+    va_list arg;
+
+    va_start(arg, format);
+    iauth_vsend(format, arg);
+    va_end(arg);
+}
+
+void iauth_vsend(const char *format, va_list list) {
+    char buffer[IAUTH_LINE + 1];
+    size_t len, len2;
+
+    memset(buffer, 0, IAUTH_LINE + 1);
+    if(iauth_debug) {
+        len2 = snprintf(buffer, IAUTH_LINE, "OUT <- ");
+        len = vsnprintf(&buffer[len2], IAUTH_LINE, format, list);
+        if(!len) return;
+        iauth_log(IAUTH_DEBUG, buffer, len + len2);
+        if(write(0, &buffer[len2], len) != len) iauth_eflog("IAuth write operation failed; errno = '%d'", errno);
+        write(0, "\r\n", 2);
+    }
+    else {
+        len = vsnprintf(buffer, IAUTH_LINE, format, list);
+        if(!len) return;
+        if(write(0, buffer, len) != len) iauth_eflog("IAuth write operation failed; errno = '%d'", errno);
+        write(0, "\r\n", 2);
+    }
+}
diff --git a/iauth_query.c b/iauth_query.c
new file mode 100644 (file)
index 0000000..7c7c714
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * Written by David Herrmann.
+ * Dedicated to the Public Domain.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include "iauth.h"
+
+/* File which is spawned on a query. */
+char *iauth_scriptfile = NULL;
+
+const struct iauth_result *iauth_query(struct iauth_client *cli) {
+    static struct iauth_result res;
+    static char buffer[IAUTH_DATALEN * 5 + 5];
+    pid_t cpid;
+    signed int fds[2], ret;
+    char ** parv;
+    char portbuf[6], portbuf2[6];
+    char c = 0;
+    char *arg, *tread;
+
+    memset(&res, 0, sizeof(res));
+    memset(portbuf, 0, 6);
+    memset(portbuf2, 0, 6);
+    if(!iauth_scriptfile) return &res;
+    if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
+        iauth_flog(IAUTH_WARNING, "socketpair() failed; errno = '%d'.", errno);
+        return NULL;
+    }
+
+    cpid = fork();
+    if(cpid < 0) {
+        close(fds[0]);
+        close(fds[1]);
+        iauth_flog(IAUTH_WARNING, "fork() failed; errno = '%d'.", errno);
+        return NULL;
+    }
+
+    if(cpid == 0) {
+        /* This is the child.
+         * Close the parents socket. Spawn the DB process with the socket
+         * set as stdout.
+         * When done, close the socket and exit the child.
+         * We directly fork() again to prevent zombies.
+         */
+        close(fds[0]);
+        cpid = fork();
+        if(cpid < 0) exit(EXIT_FAILURE);
+        if(cpid != 0) exit(EXIT_SUCCESS);
+
+        #define ISIZE 18
+        parv = iauth_malloc(sizeof(char*) * ISIZE);
+        parv[0] = iauth_scriptfile;
+        parv[1] = cli->ip?cli->ip:&c;
+        snprintf(portbuf, 5, "%hu", cli->port);
+        parv[2] = portbuf;
+        parv[3] = cli->lo_ip?cli->lo_ip:&c;
+        snprintf(portbuf2, 5, "%hu", cli->lo_port);
+        parv[4] = portbuf2;
+        parv[5] = cli->host?cli->host:&c;
+        parv[6] = cli->c_host?cli->c_host:&c;
+        parv[7] = cli->c_serv?cli->c_serv:&c;
+        parv[8] = cli->nick?cli->nick:&c;
+        parv[9] = cli->username?cli->username:&c;
+        parv[10] = cli->realname?cli->realname:&c;
+        parv[11] = cli->account?cli->account:&c;
+        parv[12] = cli->fakehost?cli->fakehost:&c;
+        parv[13] = cli->cclass?cli->cclass:&c;
+        parv[14] = cli->password?cli->password:&c;
+        parv[15] = cli->ident?cli->ident:&c;
+        parv[16] = iauth_servname;
+        parv[ISIZE - 1] = NULL;
+        #undef ISIZE
+
+        if(dup2(fds[1], 1) != -1) {
+            execvp(iauth_scriptfile, parv);
+            iauth_flog(IAUTH_WARNING, "execvp() failed; errno = '%d'.", errno);
+        }
+        else iauth_flog(IAUTH_WARNING, "dup2() failed; errno = '%d'.", errno);
+        iauth_free(parv);
+        close(fds[1]);
+        exit(EXIT_FAILURE);
+    }
+
+    /* We are the parent. Read on the file descriptor until it is closed. */
+    close(fds[1]);
+
+    /* Wait for client. The client directly forked again, so this should
+     * not *really* block.
+     * This whole mechanism prevents zombies.
+     */
+    wait(NULL);
+
+    tread = buffer;
+    if((ret = read(fds[0], buffer, sizeof(buffer))) < 1) {
+        close(fds[0]);
+        return NULL;
+    }
+    buffer[sizeof(buffer) - 1] = 0;
+
+    /* Read class. */
+    if(!(arg = strchr(tread, ' '))) {
+        close(fds[0]);
+        return NULL;
+    }
+    *arg++ = 0;
+    if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.cclass[0] = 0;
+    else strcpy(res.cclass, tread);
+
+    /* Read ident. */
+    tread = arg;
+    if(!(arg = strchr(tread, ' '))) {
+        close(fds[0]);
+        return NULL;
+    }
+    *arg++ = 0;
+    if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.ident[0] = 0;
+    else strcpy(res.ident, tread);
+
+    /* Read host. */
+    tread = arg;
+    if(!(arg = strchr(tread, ' '))) {
+        close(fds[0]);
+        return NULL;
+    }
+    *arg++ = 0;
+    if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.host[0] = 0;
+    else strcpy(res.host, tread);
+
+    /* Read ip. */
+    tread = arg;
+    if(!(arg = strchr(tread, ' '))) {
+        close(fds[0]);
+        return NULL;
+    }
+    *arg++ = 0;
+    if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.ip[0] = 0;
+    else strcpy(res.ip, tread);
+
+    /* Read modes string. */
+    tread = arg;
+    if(!*tread) {
+        close(fds[0]);
+        return NULL;
+    }
+    if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.modes[0] = 0;
+    else strcpy(res.modes, tread);
+
+    close(fds[0]);
+    return &res;
+}
+
+/* User management.
+ * Adds or removes a user.
+ */
+struct iauth_client *iauth_clients = NULL;
+unsigned int iauth_clients_size = 0;
+
+void iauth_setcap(unsigned int cap) {
+    if(iauth_clients_size) return;
+    iauth_clients = iauth_malloc(sizeof(struct iauth_client) * cap);
+    iauth_clients_size = cap;
+}
+
+void iauth_addid(signed int id) {
+    if(id < 0) return;
+    if(!iauth_clients_size || id >= iauth_clients_size) return;
+    memset(&iauth_clients[id], 0, sizeof(struct iauth_client));
+    iauth_clients[id].state_r = 1;
+    iauth_clients[id].id = id;
+}
+
+void iauth_delid(signed int id) {
+    if(id < 0) return;
+    if(!iauth_clients_size || id >= iauth_clients_size) return;
+    iauth_free(iauth_clients[id].ip);
+    iauth_free(iauth_clients[id].lo_ip);
+    iauth_free(iauth_clients[id].host);
+    iauth_free(iauth_clients[id].c_host);
+    iauth_free(iauth_clients[id].c_serv);
+    iauth_free(iauth_clients[id].nick);
+    iauth_free(iauth_clients[id].username);
+    iauth_free(iauth_clients[id].realname);
+    iauth_free(iauth_clients[id].account);
+    iauth_free(iauth_clients[id].fakehost);
+    iauth_free(iauth_clients[id].cclass);
+    iauth_free(iauth_clients[id].password);
+    iauth_free(iauth_clients[id].ident);
+    memset(&iauth_clients[id], 0, sizeof(struct iauth_client));
+}
+
+static unsigned int loc_allow = 0;
+static unsigned int loc_deny = 0;
+static unsigned int def_allow = 0;
+static unsigned int def_deny = 0;
+
+void iauth_stats_report() {
+    static unsigned int stats = 0;
+    static char buffer[512];
+
+    ++stats;
+
+    if(stats == 1) {
+        iauth_query_newconf();
+        iauth_query_config("*", "loc", "Login-On-Connect handler");
+        iauth_query_config("*", "def", "Default handler");
+    }
+    if(stats < 10) goto report;
+    else if((stats % 10) == 0) goto report;
+    else return;
+
+    report:
+    iauth_query_newstats();
+    sprintf(buffer, "Count: %u Allowed: %u Denied: %u", loc_allow + loc_deny, loc_allow, loc_deny);
+    iauth_query_stats("loc", buffer);
+    sprintf(buffer, "Count: %u Allowed: %u Denied: %u", def_allow + def_deny, def_allow, def_deny);
+    iauth_query_stats("def", buffer);
+}
+
+void iauth_stats_loc_allow() {
+    ++loc_allow;
+    iauth_stats_report();
+}
+
+void iauth_stats_loc_deny() {
+    ++loc_deny;
+    iauth_stats_report();
+}
+
+void iauth_stats_def_allow() {
+    ++def_allow;
+    iauth_stats_report();
+}
+
+void iauth_stats_def_deny() {
+    ++def_deny;
+    iauth_stats_report();
+}
+
+/* Sends a specific request to the ircd.
+ * This is a less generic but easier to use interface
+ * for the iauth_[vf]send() commands. This interface also
+ * correctly sends statistics.
+ */
+
+/* Operator Notification: > :<message text> */
+extern void iauth_query_fnotify(const char *format, ...) {
+    va_list arg;
+
+    va_start(arg, format);
+    iauth_query_vnotify(format, arg);
+    va_end(arg);
+}
+extern void iauth_query_vnotify(const char *format, va_list list) {
+    char buffer[IAUTH_LINE + 1];
+
+    buffer[IAUTH_LINE] = 0;
+    sprintf(buffer, "> :");
+    vsnprintf(buffer, IAUTH_LINE - 4, format, list);
+    iauth_fsend(buffer);
+}
+
+/* Set Debug Level: G <level> */
+extern void iauth_query_debug(unsigned int debuglevel) {
+    iauth_fsend("G %u", debuglevel);
+}
+
+/* Set Policy Options: O <options> */
+extern void iauth_query_policy(const char *policy) {
+    iauth_fsend("O %s", policy);
+}
+
+/* iauth Program Version: V :<version string> */
+extern void iauth_query_version(const char *version) {
+    iauth_fsend("V :%s", version);
+}
+
+/* Start of new configuration: a */
+extern void iauth_query_newconf() {
+    iauth_fsend("a");
+}
+
+/* Configuration Information: A <hosts?> <module> :<options> */
+extern void iauth_query_config(const char *hosts, const char *module, const char *value) {
+    iauth_fsend("A %s %s :%s", hosts, module, value);
+}
+
+/* Start of new statistics: s */
+extern void iauth_query_newstats() {
+    iauth_fsend("s");
+}
+
+/* Statistics Information: S <module> :<module information> */
+extern void iauth_query_stats(const char *module, const char *value) {
+    iauth_fsend("S %s :%s", module, value);
+}
+
+/* Forced Username: o <id> <remoteip> <remoteport> <username> */
+extern void iauth_query_set_username(signed int id, const char *username) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("o %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
+}
+
+/* Trusted Username: U <id> <remoteip> <remoteport> <username> */
+extern void iauth_query_trust_username(signed int id, const char *username) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("U %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
+}
+
+/* Untrusted Username: u <id> <remoteip> <remoteport> <username> */
+extern void iauth_query_distrust_username(signed int id, const char *username) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("u %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
+}
+
+/* Client Hostname: N <id> <remoteip> <remoteport> <hostname> */
+extern void iauth_query_sethost(signed int id, const char *hostname) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("N %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, hostname);
+}
+
+/* Client IP Address: I <id> <currentip> <remoteport> <newip> */
+extern void iauth_query_setip(signed int id, const char *ip) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("I %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, ip);
+    iauth_free(iauth_clients[id].ip);
+    iauth_clients[id].ip = iauth_strdup(ip);
+}
+
+/* Adjust User Mode: M <id> <remoteip> <remoteport> +<mode changes> */
+extern void iauth_query_setmodes(signed int id, const char *modes) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("M %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, modes);
+}
+
+/* Challenge User: C <id> <remoteip> <remoteport> :<challenge string> */
+extern void iauth_query_challenge(signed int id, const char *challenge) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("C %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, challenge);
+}
+
+/* Quietly Kill Client: k <id> <remoteip> <remoteport> :<reason> */
+extern void iauth_query_reject(signed int id, const char *reason) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("k %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, reason);
+    iauth_delid(id);
+}
+
+/* Kill Client: K <id> <remoteip> <remoteport> :<reason> */
+extern void iauth_query_kill(signed int id, const char *reason) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    iauth_fsend("K %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, reason);
+    iauth_delid(id);
+}
+
+/* Done Checking: D <id> <remoteip> <remoteport> [class] */
+extern void iauth_query_assign(signed int id, const char *cclass) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    if(cclass)
+        iauth_fsend("D %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, cclass);
+    else
+        iauth_fsend("D %d %s %hu", id, iauth_clients[id].ip, iauth_clients[id].port);
+    iauth_delid(id);
+}
+
+/* Registered User: R <id> <remoteip> <remoteport> <account> [class] */
+extern void iauth_query_register(signed int id, const char *account, const char *cclass) {
+    if(id < 0 || id >= iauth_clients_size) return;
+    if(cclass)
+        iauth_fsend("D %d %s %hu %s %s", id, iauth_clients[id].ip, iauth_clients[id].port, account, cclass);
+    else
+        iauth_fsend("D %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, account);
+    iauth_delid(id);
+}
+