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