1 /************************************************************************
2 * IRC - Internet Relay Chat, iauth/mod_lhex.c
3 * Copyright (C) 1998-1999 Christophe Kalt and Andrew Snare
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #ifndef lint
21 static const volatile char rcsid[] = "@(#)$Id mod_lhex.c,v 1.12 1999/02/06 21:43:52 kalt Exp $";
22 #endif
23
24 #include "os.h"
25 #include "a_defines.h"
26 #define MOD_LHEX_C
27 #include "a_externs.h"
28 #undef MOD_LHEX_C
29
30 /****************************** PRIVATE *************************************/
31 #define LHEXPORT 9674
32
33 struct lhex_private
34 {
35 /* stats */
36 u_int ok, banned;
37 u_int tried, clean, timeout;
38 };
39
40 /******************************** PUBLIC ************************************/
41
42 /*
43 * lhex_init
44 *
45 * This procedure is called when a particular module is loaded.
46 * Returns NULL if everything went fine,
47 * an error message otherwise.
48 */
lhex_init(AnInstance * self)49 static char *lhex_init(AnInstance *self)
50 {
51 struct lhex_private *mydata;
52
53 #if defined(INET6)
54 return "IPv6 unsupported.";
55 #endif
56 if(self->opt == NULL)
57 return "Aie! no option(s): no LHEx server to connect to!";
58 if(!inetaton(self->opt,NULL))
59 return "Aie! Option wasn't a valid IP address!";
60
61 /* Allocate the module data */
62 mydata = (struct lhex_private *) malloc(sizeof(struct lhex_private));
63 bzero((char *) mydata, sizeof(struct lhex_private));
64
65 self->popt = mystrdup(self->opt);
66 self->data = mydata;
67 return NULL;
68 }
69
70 /*
71 * lhex_release
72 *
73 * This procedure is called when a particular module is unloaded.
74 */
lhex_release(AnInstance * self)75 static void lhex_release(AnInstance *self)
76 {
77 struct lhex_private *mydata = self->data;
78 free(mydata);
79 free(self->popt);
80 }
81
82 /*
83 * lhex_stats
84 *
85 * This procedure is called regularly to update statistics sent to ircd.
86 */
lhex_stats(AnInstance * self)87 static void lhex_stats(AnInstance *self)
88 {
89 struct lhex_private *mydata = self->data;
90
91 sendto_ircd("S lhex ok %u banned %u", mydata->ok, mydata->banned);
92 sendto_ircd("S lhex tried %u aborted %u / %u",
93 mydata->tried, mydata->clean, mydata->timeout);
94 }
95
96 /*
97 * lhex_start
98 *
99 * This procedure is called to start the LHEx check procedure.
100 * Returns 0 if everything went fine,
101 * anything else otherwise (nothing to be done, or failure)
102 *
103 * It is responsible for sending error messages where appropriate.
104 * In case of failure, it's responsible for cleaning up (e.g. lhex_clean
105 * will NOT be called)
106 */
lhex_start(u_int cl)107 static int lhex_start(u_int cl)
108 {
109 char *error;
110 int fd;
111 struct lhex_private *mydata = cldata[cl].instance->data;
112
113 if (cldata[cl].state & A_DENY)
114 {
115 /* no point of doing anything */
116 DebugLog((ALOG_DLHEX, 0,
117 "lhex_start(%d): A_DENY already set ", cl));
118 return -1;
119 }
120
121 DebugLog((ALOG_DLHEX, 0, "lhex_start(%d): Connecting to %s", cl,
122 cldata[cl].instance->opt));
123 mydata->tried += 1;
124 fd= tcp_connect(cldata[cl].ourip, cldata[cl].instance->opt,
125 LHEXPORT, &error);
126 if (fd < 0)
127 {
128 DebugLog((ALOG_DLHEX, 0,
129 "lhex_start(%d): tcp_connect() reported %s",
130 cl, error));
131 return -1;
132 }
133
134 cldata[cl].wfd = fd; /*so that lhex_work() is called when connected*/
135 return 0;
136 }
137
138 /*
139 * lhex_work
140 *
141 * This procedure is called whenever there's new data in the buffer.
142 * Returns 0 if everything went fine, and there is more work to be done,
143 * Returns -1 if the module has finished its work (and cleaned up).
144 *
145 * It is responsible for sending error messages where appropriate.
146 */
lhex_work(u_int cl)147 static int lhex_work(u_int cl)
148 {
149 DebugLog((ALOG_DLHEX, 0, "lhex_work(%d): %d %d buflen=%d", cl,
150 cldata[cl].rfd, cldata[cl].wfd, cldata[cl].buflen));
151 if (cldata[cl].wfd > 0)
152 {
153 /*
154 ** We haven't sent the query yet, the connection was just
155 ** established.
156 */
157 char query[3+7+6+4+USERLEN+2*HOSTLEN+8+3];/*strlen(atoi(cl))<=8*/
158 char *ident = cldata[cl].authuser;
159
160 /* This is part of every request */
161 sprintf(query, "id:%u ip:%s", cl, cldata[cl].itsip);
162
163 /* These bits are optional, depending on what's known */
164 if (ident)
165 {
166 strcat(query, " ident:");
167 strcat(query, ident);
168 }
169 if (cldata[cl].state & A_GOTH)
170 {
171 strcat(query, " host:");
172 strcat(query, cldata[cl].host);
173 }
174 /* Terminate the request */
175 strcat(query, "\r\n");
176
177 DebugLog((ALOG_DLHEX, 0, "lhex_work(%u): Sending query [%s]",
178 cl, query));
179 if (write(cldata[cl].wfd, query, strlen(query)) < 0)
180 {
181 /* most likely the connection failed */
182 DebugLog((ALOG_DLHEX, 0,
183 "lhex_work(%u): write() failed: %s",
184 cl, strerror(errno)));
185 close(cldata[cl].wfd);
186 cldata[cl].rfd = cldata[cl].wfd = 0;
187 return 1;
188 }
189 cldata[cl].rfd = cldata[cl].wfd;
190 cldata[cl].wfd = 0;
191 }
192 else
193 {
194 /* data's in from the other end */
195 char *ch, *nch;
196 u_int id;
197 int retval = 0;
198
199 cldata[cl].inbuffer[cldata[cl].buflen] = '\0';
200 nch = cldata[cl].inbuffer;
201 while((nch < (cldata[cl].inbuffer + cldata[cl].buflen)) &&
202 (ch = index(nch, '\r')) && !retval)
203 {
204 char *och = nch;
205 nch = ch+2; /* Skip the \r\n */
206 *ch = '\0';
207 DebugLog((ALOG_DLHEX, 0, "lhex_work(%u): Got [%s]",
208 cl, och));
209
210 /* Have a go at parsing the return info */
211 if(sscanf(och, "%u", &id) != 1)
212 DebugLog((ALOG_DLHEX, 0, "lhex_work(%u): "
213 "Malformed data!", cl));
214 else
215 {
216 struct lhex_private *d=cldata[cl].instance->data;
217 ch = index(och, ':');
218 while(isspace(*(++ch)));
219 if(!strcmp(ch,"OK"))
220 {
221 d->ok++;
222 DebugLog((ALOG_DLHEX, 0,
223 "lhex_work(%u): OK", id));
224 close(cldata[cl].rfd);
225 cldata[cl].rfd = 0;
226 retval = -1;
227 }
228 else if(!strcmp(ch,"Not OK"))
229 {
230 d->banned++;
231 DebugLog((ALOG_DLHEX, 0,
232 "lhex_work(%u): Not OK", id));
233 cldata[cl].state |= A_DENY;
234 /* I really wish we could send the
235 client a "reason" here :P */
236 sendto_ircd("K %d %s %u ", cl,
237 cldata[cl].itsip,
238 cldata[cl].itsport);
239 close(cldata[cl].rfd);
240 cldata[cl].rfd = 0;
241 retval = -1;
242 }
243 else
244 {
245 #if 0
246 /* Call this info for the client */
247 sendto_ircd("I %d %s %u NOTICE AUTH :%s",
248 cl, cldata[cl].itsip,
249 cldata[cl].itsport, ch);
250 #endif
251 retval = 0;
252 }
253 }
254 }
255 return retval;
256 }
257 return 0;
258 }
259
260 /*
261 * lhex_clean
262 *
263 * This procedure is called whenever the module should interrupt its work.
264 * It is responsible for cleaning up any allocated data, and in particular
265 * closing file descriptors.
266 */
lhex_clean(u_int cl)267 static void lhex_clean(u_int cl)
268 {
269 struct lhex_private *mydata = cldata[cl].instance->data;
270
271 mydata->clean += 1;
272 DebugLog((ALOG_DLHEX, 0, "lhex_clean(%d): cleaning up", cl));
273 /*
274 ** only one of rfd and wfd may be set at the same time,
275 ** in any case, they would be the same fd, so only close() once
276 */
277 if (cldata[cl].rfd)
278 close(cldata[cl].rfd);
279 else if (cldata[cl].wfd)
280 close(cldata[cl].wfd);
281 cldata[cl].rfd = cldata[cl].wfd = 0;
282 }
283
284 /*
285 * lhex_timeout
286 *
287 * This procedure is called whenever the timeout set by the module is
288 * reached.
289 *
290 * Returns 0 if things are okay, -1 if check was aborted.
291 */
lhex_timeout(u_int cl)292 static int lhex_timeout(u_int cl)
293 {
294 struct lhex_private *mydata = cldata[cl].instance->data;
295
296 mydata->timeout += 1;
297 DebugLog((ALOG_DLHEX, 0, "lhex_timeout(%d): calling lhex_clean ", cl));
298 lhex_clean(cl);
299 return -1;
300 }
301
302 aModule Module_lhex =
303 { "lhex", lhex_init, lhex_release, lhex_stats,
304 lhex_start, lhex_work, lhex_timeout, lhex_clean };
305
306