7c7c7145bc0e54c131e283dc16c77cf81b0b23a3
[iauth.git] / iauth_query.c
1 /*
2  * Written by David Herrmann.
3  * Dedicated to the Public Domain.
4  */
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <errno.h>
10
11 #include <unistd.h>
12 #include <sys/types.h>
13 #include <sys/socket.h>
14 #include <sys/wait.h>
15
16 #include "iauth.h"
17
18 /* File which is spawned on a query. */
19 char *iauth_scriptfile = NULL;
20
21 const struct iauth_result *iauth_query(struct iauth_client *cli) {
22     static struct iauth_result res;
23     static char buffer[IAUTH_DATALEN * 5 + 5];
24     pid_t cpid;
25     signed int fds[2], ret;
26     char ** parv;
27     char portbuf[6], portbuf2[6];
28     char c = 0;
29     char *arg, *tread;
30
31     memset(&res, 0, sizeof(res));
32     memset(portbuf, 0, 6);
33     memset(portbuf2, 0, 6);
34     if(!iauth_scriptfile) return &res;
35     if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) {
36         iauth_flog(IAUTH_WARNING, "socketpair() failed; errno = '%d'.", errno);
37         return NULL;
38     }
39
40     cpid = fork();
41     if(cpid < 0) {
42         close(fds[0]);
43         close(fds[1]);
44         iauth_flog(IAUTH_WARNING, "fork() failed; errno = '%d'.", errno);
45         return NULL;
46     }
47
48     if(cpid == 0) {
49         /* This is the child.
50          * Close the parents socket. Spawn the DB process with the socket
51          * set as stdout.
52          * When done, close the socket and exit the child.
53          * We directly fork() again to prevent zombies.
54          */
55         close(fds[0]);
56         cpid = fork();
57         if(cpid < 0) exit(EXIT_FAILURE);
58         if(cpid != 0) exit(EXIT_SUCCESS);
59
60         #define ISIZE 18
61         parv = iauth_malloc(sizeof(char*) * ISIZE);
62         parv[0] = iauth_scriptfile;
63         parv[1] = cli->ip?cli->ip:&c;
64         snprintf(portbuf, 5, "%hu", cli->port);
65         parv[2] = portbuf;
66         parv[3] = cli->lo_ip?cli->lo_ip:&c;
67         snprintf(portbuf2, 5, "%hu", cli->lo_port);
68         parv[4] = portbuf2;
69         parv[5] = cli->host?cli->host:&c;
70         parv[6] = cli->c_host?cli->c_host:&c;
71         parv[7] = cli->c_serv?cli->c_serv:&c;
72         parv[8] = cli->nick?cli->nick:&c;
73         parv[9] = cli->username?cli->username:&c;
74         parv[10] = cli->realname?cli->realname:&c;
75         parv[11] = cli->account?cli->account:&c;
76         parv[12] = cli->fakehost?cli->fakehost:&c;
77         parv[13] = cli->cclass?cli->cclass:&c;
78         parv[14] = cli->password?cli->password:&c;
79         parv[15] = cli->ident?cli->ident:&c;
80         parv[16] = iauth_servname;
81         parv[ISIZE - 1] = NULL;
82         #undef ISIZE
83
84         if(dup2(fds[1], 1) != -1) {
85             execvp(iauth_scriptfile, parv);
86             iauth_flog(IAUTH_WARNING, "execvp() failed; errno = '%d'.", errno);
87         }
88         else iauth_flog(IAUTH_WARNING, "dup2() failed; errno = '%d'.", errno);
89         iauth_free(parv);
90         close(fds[1]);
91         exit(EXIT_FAILURE);
92     }
93
94     /* We are the parent. Read on the file descriptor until it is closed. */
95     close(fds[1]);
96
97     /* Wait for client. The client directly forked again, so this should
98      * not *really* block.
99      * This whole mechanism prevents zombies.
100      */
101     wait(NULL);
102
103     tread = buffer;
104     if((ret = read(fds[0], buffer, sizeof(buffer))) < 1) {
105         close(fds[0]);
106         return NULL;
107     }
108     buffer[sizeof(buffer) - 1] = 0;
109
110     /* Read class. */
111     if(!(arg = strchr(tread, ' '))) {
112         close(fds[0]);
113         return NULL;
114     }
115     *arg++ = 0;
116     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.cclass[0] = 0;
117     else strcpy(res.cclass, tread);
118
119     /* Read ident. */
120     tread = arg;
121     if(!(arg = strchr(tread, ' '))) {
122         close(fds[0]);
123         return NULL;
124     }
125     *arg++ = 0;
126     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.ident[0] = 0;
127     else strcpy(res.ident, tread);
128
129     /* Read host. */
130     tread = arg;
131     if(!(arg = strchr(tread, ' '))) {
132         close(fds[0]);
133         return NULL;
134     }
135     *arg++ = 0;
136     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.host[0] = 0;
137     else strcpy(res.host, tread);
138
139     /* Read ip. */
140     tread = arg;
141     if(!(arg = strchr(tread, ' '))) {
142         close(fds[0]);
143         return NULL;
144     }
145     *arg++ = 0;
146     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.ip[0] = 0;
147     else strcpy(res.ip, tread);
148
149     /* Read modes string. */
150     tread = arg;
151     if(!*tread) {
152         close(fds[0]);
153         return NULL;
154     }
155     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.modes[0] = 0;
156     else strcpy(res.modes, tread);
157
158     close(fds[0]);
159     return &res;
160 }
161
162 /* User management.
163  * Adds or removes a user.
164  */
165 struct iauth_client *iauth_clients = NULL;
166 unsigned int iauth_clients_size = 0;
167
168 void iauth_setcap(unsigned int cap) {
169     if(iauth_clients_size) return;
170     iauth_clients = iauth_malloc(sizeof(struct iauth_client) * cap);
171     iauth_clients_size = cap;
172 }
173
174 void iauth_addid(signed int id) {
175     if(id < 0) return;
176     if(!iauth_clients_size || id >= iauth_clients_size) return;
177     memset(&iauth_clients[id], 0, sizeof(struct iauth_client));
178     iauth_clients[id].state_r = 1;
179     iauth_clients[id].id = id;
180 }
181
182 void iauth_delid(signed int id) {
183     if(id < 0) return;
184     if(!iauth_clients_size || id >= iauth_clients_size) return;
185     iauth_free(iauth_clients[id].ip);
186     iauth_free(iauth_clients[id].lo_ip);
187     iauth_free(iauth_clients[id].host);
188     iauth_free(iauth_clients[id].c_host);
189     iauth_free(iauth_clients[id].c_serv);
190     iauth_free(iauth_clients[id].nick);
191     iauth_free(iauth_clients[id].username);
192     iauth_free(iauth_clients[id].realname);
193     iauth_free(iauth_clients[id].account);
194     iauth_free(iauth_clients[id].fakehost);
195     iauth_free(iauth_clients[id].cclass);
196     iauth_free(iauth_clients[id].password);
197     iauth_free(iauth_clients[id].ident);
198     memset(&iauth_clients[id], 0, sizeof(struct iauth_client));
199 }
200
201 static unsigned int loc_allow = 0;
202 static unsigned int loc_deny = 0;
203 static unsigned int def_allow = 0;
204 static unsigned int def_deny = 0;
205
206 void iauth_stats_report() {
207     static unsigned int stats = 0;
208     static char buffer[512];
209
210     ++stats;
211
212     if(stats == 1) {
213         iauth_query_newconf();
214         iauth_query_config("*", "loc", "Login-On-Connect handler");
215         iauth_query_config("*", "def", "Default handler");
216     }
217     if(stats < 10) goto report;
218     else if((stats % 10) == 0) goto report;
219     else return;
220
221     report:
222     iauth_query_newstats();
223     sprintf(buffer, "Count: %u Allowed: %u Denied: %u", loc_allow + loc_deny, loc_allow, loc_deny);
224     iauth_query_stats("loc", buffer);
225     sprintf(buffer, "Count: %u Allowed: %u Denied: %u", def_allow + def_deny, def_allow, def_deny);
226     iauth_query_stats("def", buffer);
227 }
228
229 void iauth_stats_loc_allow() {
230     ++loc_allow;
231     iauth_stats_report();
232 }
233
234 void iauth_stats_loc_deny() {
235     ++loc_deny;
236     iauth_stats_report();
237 }
238
239 void iauth_stats_def_allow() {
240     ++def_allow;
241     iauth_stats_report();
242 }
243
244 void iauth_stats_def_deny() {
245     ++def_deny;
246     iauth_stats_report();
247 }
248
249 /* Sends a specific request to the ircd.
250  * This is a less generic but easier to use interface
251  * for the iauth_[vf]send() commands. This interface also
252  * correctly sends statistics.
253  */
254
255 /* Operator Notification: > :<message text> */
256 extern void iauth_query_fnotify(const char *format, ...) {
257     va_list arg;
258
259     va_start(arg, format);
260     iauth_query_vnotify(format, arg);
261     va_end(arg);
262 }
263 extern void iauth_query_vnotify(const char *format, va_list list) {
264     char buffer[IAUTH_LINE + 1];
265
266     buffer[IAUTH_LINE] = 0;
267     sprintf(buffer, "> :");
268     vsnprintf(buffer, IAUTH_LINE - 4, format, list);
269     iauth_fsend(buffer);
270 }
271
272 /* Set Debug Level: G <level> */
273 extern void iauth_query_debug(unsigned int debuglevel) {
274     iauth_fsend("G %u", debuglevel);
275 }
276
277 /* Set Policy Options: O <options> */
278 extern void iauth_query_policy(const char *policy) {
279     iauth_fsend("O %s", policy);
280 }
281
282 /* iauth Program Version: V :<version string> */
283 extern void iauth_query_version(const char *version) {
284     iauth_fsend("V :%s", version);
285 }
286
287 /* Start of new configuration: a */
288 extern void iauth_query_newconf() {
289     iauth_fsend("a");
290 }
291
292 /* Configuration Information: A <hosts?> <module> :<options> */
293 extern void iauth_query_config(const char *hosts, const char *module, const char *value) {
294     iauth_fsend("A %s %s :%s", hosts, module, value);
295 }
296
297 /* Start of new statistics: s */
298 extern void iauth_query_newstats() {
299     iauth_fsend("s");
300 }
301
302 /* Statistics Information: S <module> :<module information> */
303 extern void iauth_query_stats(const char *module, const char *value) {
304     iauth_fsend("S %s :%s", module, value);
305 }
306
307 /* Forced Username: o <id> <remoteip> <remoteport> <username> */
308 extern void iauth_query_set_username(signed int id, const char *username) {
309     if(id < 0 || id >= iauth_clients_size) return;
310     iauth_fsend("o %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
311 }
312
313 /* Trusted Username: U <id> <remoteip> <remoteport> <username> */
314 extern void iauth_query_trust_username(signed int id, const char *username) {
315     if(id < 0 || id >= iauth_clients_size) return;
316     iauth_fsend("U %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
317 }
318
319 /* Untrusted Username: u <id> <remoteip> <remoteport> <username> */
320 extern void iauth_query_distrust_username(signed int id, const char *username) {
321     if(id < 0 || id >= iauth_clients_size) return;
322     iauth_fsend("u %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
323 }
324
325 /* Client Hostname: N <id> <remoteip> <remoteport> <hostname> */
326 extern void iauth_query_sethost(signed int id, const char *hostname) {
327     if(id < 0 || id >= iauth_clients_size) return;
328     iauth_fsend("N %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, hostname);
329 }
330
331 /* Client IP Address: I <id> <currentip> <remoteport> <newip> */
332 extern void iauth_query_setip(signed int id, const char *ip) {
333     if(id < 0 || id >= iauth_clients_size) return;
334     iauth_fsend("I %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, ip);
335     iauth_free(iauth_clients[id].ip);
336     iauth_clients[id].ip = iauth_strdup(ip);
337 }
338
339 /* Adjust User Mode: M <id> <remoteip> <remoteport> +<mode changes> */
340 extern void iauth_query_setmodes(signed int id, const char *modes) {
341     if(id < 0 || id >= iauth_clients_size) return;
342     iauth_fsend("M %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, modes);
343 }
344
345 /* Challenge User: C <id> <remoteip> <remoteport> :<challenge string> */
346 extern void iauth_query_challenge(signed int id, const char *challenge) {
347     if(id < 0 || id >= iauth_clients_size) return;
348     iauth_fsend("C %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, challenge);
349 }
350
351 /* Quietly Kill Client: k <id> <remoteip> <remoteport> :<reason> */
352 extern void iauth_query_reject(signed int id, const char *reason) {
353     if(id < 0 || id >= iauth_clients_size) return;
354     iauth_fsend("k %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, reason);
355     iauth_delid(id);
356 }
357
358 /* Kill Client: K <id> <remoteip> <remoteport> :<reason> */
359 extern void iauth_query_kill(signed int id, const char *reason) {
360     if(id < 0 || id >= iauth_clients_size) return;
361     iauth_fsend("K %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, reason);
362     iauth_delid(id);
363 }
364
365 /* Done Checking: D <id> <remoteip> <remoteport> [class] */
366 extern void iauth_query_assign(signed int id, const char *cclass) {
367     if(id < 0 || id >= iauth_clients_size) return;
368     if(cclass)
369         iauth_fsend("D %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, cclass);
370     else
371         iauth_fsend("D %d %s %hu", id, iauth_clients[id].ip, iauth_clients[id].port);
372     iauth_delid(id);
373 }
374
375 /* Registered User: R <id> <remoteip> <remoteport> <account> [class] */
376 extern void iauth_query_register(signed int id, const char *account, const char *cclass) {
377     if(id < 0 || id >= iauth_clients_size) return;
378     if(cclass)
379         iauth_fsend("D %d %s %hu %s %s", id, iauth_clients[id].ip, iauth_clients[id].port, account, cclass);
380     else
381         iauth_fsend("D %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, account);
382     iauth_delid(id);
383 }
384