give the possibility to pass an error message to the user
[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, "error") == 0) {
117          strcpy(res.cclass, "error");
118         tread = arg;
119     if(!(arg = strchr(tread, '%'))) {
120         close(fds[0]);
121         return NULL;
122     }
123     *arg++ = 0;
124         
125          strcpy(res.str, tread);
126          return &res;
127         }
128     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.cclass[0] = 0;
129     else strcpy(res.cclass, tread);
130
131     /* Read ident. */
132     tread = arg;
133     if(!(arg = strchr(tread, ' '))) {
134         close(fds[0]);
135         return NULL;
136     }
137     *arg++ = 0;
138     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.ident[0] = 0;
139     else strcpy(res.ident, tread);
140
141     /* Read host. */
142     tread = arg;
143     if(!(arg = strchr(tread, ' '))) {
144         close(fds[0]);
145         return NULL;
146     }
147     *arg++ = 0;
148     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.host[0] = 0;
149     else strcpy(res.host, tread);
150
151     /* Read ip. */
152     tread = arg;
153     if(!(arg = strchr(tread, ' '))) {
154         close(fds[0]);
155         return NULL;
156     }
157     *arg++ = 0;
158     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.ip[0] = 0;
159     else strcpy(res.ip, tread);
160
161     /* Read modes string. */
162     tread = arg;
163     if(!(arg = strchr(tread, '%'))) {
164         close(fds[0]);
165         return NULL;
166     }
167     *arg++ = 0;
168     if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.modes[0] = 0;
169     else strcpy(res.modes, tread);
170
171     close(fds[0]);
172     return &res;
173 }
174
175 /* User management.
176  * Adds or removes a user.
177  */
178 struct iauth_client *iauth_clients = NULL;
179 unsigned int iauth_clients_size = 0;
180
181 void iauth_setcap(unsigned int cap) {
182     if(iauth_clients_size) return;
183     iauth_clients = iauth_malloc(sizeof(struct iauth_client) * cap);
184     iauth_clients_size = cap;
185 }
186
187 void iauth_addid(signed int id) {
188     if(id < 0) return;
189     if(!iauth_clients_size || id >= iauth_clients_size) return;
190     memset(&iauth_clients[id], 0, sizeof(struct iauth_client));
191     iauth_clients[id].state_r = 1;
192     iauth_clients[id].id = id;
193 }
194
195 void iauth_delid(signed int id) {
196     if(id < 0) return;
197     if(!iauth_clients_size || id >= iauth_clients_size) return;
198     iauth_free(iauth_clients[id].ip);
199     iauth_free(iauth_clients[id].lo_ip);
200     iauth_free(iauth_clients[id].host);
201     iauth_free(iauth_clients[id].c_host);
202     iauth_free(iauth_clients[id].c_serv);
203     iauth_free(iauth_clients[id].nick);
204     iauth_free(iauth_clients[id].username);
205     iauth_free(iauth_clients[id].realname);
206     iauth_free(iauth_clients[id].account);
207     iauth_free(iauth_clients[id].fakehost);
208     iauth_free(iauth_clients[id].cclass);
209     iauth_free(iauth_clients[id].password);
210     iauth_free(iauth_clients[id].ident);
211     memset(&iauth_clients[id], 0, sizeof(struct iauth_client));
212 }
213
214 static unsigned int loc_allow = 0;
215 static unsigned int loc_deny = 0;
216 static unsigned int def_allow = 0;
217 static unsigned int def_deny = 0;
218
219 void iauth_stats_report() {
220     static unsigned int stats = 0;
221     static char buffer[512];
222
223     ++stats;
224
225     if(stats == 1) {
226         iauth_query_newconf();
227         iauth_query_config("*", "loc", "Login-On-Connect handler");
228         iauth_query_config("*", "def", "Default handler");
229     }
230     if(stats < 10) goto report;
231     else if((stats % 10) == 0) goto report;
232     else return;
233
234     report:
235     iauth_query_newstats();
236     sprintf(buffer, "Count: %u Allowed: %u Denied: %u", loc_allow + loc_deny, loc_allow, loc_deny);
237     iauth_query_stats("loc", buffer);
238     sprintf(buffer, "Count: %u Allowed: %u Denied: %u", def_allow + def_deny, def_allow, def_deny);
239     iauth_query_stats("def", buffer);
240 }
241
242 void iauth_stats_loc_allow() {
243     ++loc_allow;
244     iauth_stats_report();
245 }
246
247 void iauth_stats_loc_deny() {
248     ++loc_deny;
249     iauth_stats_report();
250 }
251
252 void iauth_stats_def_allow() {
253     ++def_allow;
254     iauth_stats_report();
255 }
256
257 void iauth_stats_def_deny() {
258     ++def_deny;
259     iauth_stats_report();
260 }
261
262 /* Sends a specific request to the ircd.
263  * This is a less generic but easier to use interface
264  * for the iauth_[vf]send() commands. This interface also
265  * correctly sends statistics.
266  */
267
268 /* Operator Notification: > :<message text> */
269 extern void iauth_query_fnotify(const char *format, ...) {
270     va_list arg;
271
272     va_start(arg, format);
273     iauth_query_vnotify(format, arg);
274     va_end(arg);
275 }
276 extern void iauth_query_vnotify(const char *format, va_list list) {
277     char buffer[IAUTH_LINE + 1];
278
279     buffer[IAUTH_LINE] = 0;
280     sprintf(buffer, "> :");
281     vsnprintf(buffer, IAUTH_LINE - 4, format, list);
282     iauth_fsend(buffer);
283 }
284
285 /* Set Debug Level: G <level> */
286 extern void iauth_query_debug(unsigned int debuglevel) {
287     iauth_fsend("G %u", debuglevel);
288 }
289
290 /* Set Policy Options: O <options> */
291 extern void iauth_query_policy(const char *policy) {
292     iauth_fsend("O %s", policy);
293 }
294
295 /* iauth Program Version: V :<version string> */
296 extern void iauth_query_version(const char *version) {
297     iauth_fsend("V :%s", version);
298 }
299
300 /* Start of new configuration: a */
301 extern void iauth_query_newconf() {
302     iauth_fsend("a");
303 }
304
305 /* Configuration Information: A <hosts?> <module> :<options> */
306 extern void iauth_query_config(const char *hosts, const char *module, const char *value) {
307     iauth_fsend("A %s %s :%s", hosts, module, value);
308 }
309
310 /* Start of new statistics: s */
311 extern void iauth_query_newstats() {
312     iauth_fsend("s");
313 }
314
315 /* Statistics Information: S <module> :<module information> */
316 extern void iauth_query_stats(const char *module, const char *value) {
317     iauth_fsend("S %s :%s", module, value);
318 }
319
320 /* Forced Username: o <id> <remoteip> <remoteport> <username> */
321 extern void iauth_query_set_username(signed int id, const char *username) {
322     if(id < 0 || id >= iauth_clients_size) return;
323     iauth_fsend("o %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
324 }
325
326 /* Trusted Username: U <id> <remoteip> <remoteport> <username> */
327 extern void iauth_query_trust_username(signed int id, const char *username) {
328     if(id < 0 || id >= iauth_clients_size) return;
329     iauth_fsend("U %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
330 }
331
332 /* Untrusted Username: u <id> <remoteip> <remoteport> <username> */
333 extern void iauth_query_distrust_username(signed int id, const char *username) {
334     if(id < 0 || id >= iauth_clients_size) return;
335     iauth_fsend("u %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username);
336 }
337
338 /* Client Hostname: N <id> <remoteip> <remoteport> <hostname> */
339 extern void iauth_query_sethost(signed int id, const char *hostname) {
340     if(id < 0 || id >= iauth_clients_size) return;
341     iauth_fsend("N %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, hostname);
342 }
343
344 /* Client IP Address: I <id> <currentip> <remoteport> <newip> */
345 extern void iauth_query_setip(signed int id, const char *ip) {
346     if(id < 0 || id >= iauth_clients_size) return;
347     iauth_fsend("I %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, ip);
348     iauth_free(iauth_clients[id].ip);
349     iauth_clients[id].ip = iauth_strdup(ip);
350 }
351
352 /* Adjust User Mode: M <id> <remoteip> <remoteport> +<mode changes> */
353 extern void iauth_query_setmodes(signed int id, const char *modes) {
354     if(id < 0 || id >= iauth_clients_size) return;
355     iauth_fsend("M %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, modes);
356 }
357
358 /* Challenge User: C <id> <remoteip> <remoteport> :<challenge string> */
359 extern void iauth_query_challenge(signed int id, const char *challenge) {
360     if(id < 0 || id >= iauth_clients_size) return;
361     iauth_fsend("C %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, challenge);
362 }
363
364 /* Quietly Kill Client: k <id> <remoteip> <remoteport> :<reason> */
365 extern void iauth_query_reject(signed int id, const char *reason) {
366     if(id < 0 || id >= iauth_clients_size) return;
367     iauth_fsend("k %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, reason);
368     iauth_delid(id);
369 }
370
371 /* Kill Client: K <id> <remoteip> <remoteport> :<reason> */
372 extern void iauth_query_kill(signed int id, const char *reason) {
373     if(id < 0 || id >= iauth_clients_size) return;
374     iauth_fsend("K %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, reason);
375     iauth_delid(id);
376 }
377
378 /* Done Checking: D <id> <remoteip> <remoteport> [class] */
379 extern void iauth_query_assign(signed int id, const char *cclass) {
380     if(id < 0 || id >= iauth_clients_size) return;
381     if(cclass)
382         iauth_fsend("D %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, cclass);
383     else
384         iauth_fsend("D %d %s %hu", id, iauth_clients[id].ip, iauth_clients[id].port);
385     iauth_delid(id);
386 }
387
388 /* Registered User: R <id> <remoteip> <remoteport> <account> [class] */
389 extern void iauth_query_register(signed int id, const char *account, const char *cclass) {
390     if(id < 0 || id >= iauth_clients_size) return;
391     if(cclass)
392         iauth_fsend("D %d %s %hu %s %s", id, iauth_clients[id].ip, iauth_clients[id].port, account, cclass);
393     else
394         iauth_fsend("D %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, account);
395     iauth_delid(id);
396 }
397