1 /*
2  * atheme-services: A collection of minimalist IRC services
3  * servers.c: Server and network state tracking.
4  *
5  * Copyright (c) 2005-2007 Atheme Project (http://www.atheme.org)
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
12  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
13  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
14  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
15  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
16  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
17  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
19  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
20  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
21  * POSSIBILITY OF SUCH DAMAGE.
22  */
23 
24 #include "atheme.h"
25 
26 mowgli_patricia_t *sidlist;
27 mowgli_patricia_t *servlist;
28 mowgli_list_t tldlist;
29 
30 mowgli_heap_t *serv_heap;
31 mowgli_heap_t *tld_heap;
32 
33 static void server_delete_serv(server_t *s);
34 
35 /*
36  * init_servers()
37  *
38  * Initializes the server heap and server/sid DTree structures.
39  *
40  * Inputs:
41  *     - nothing
42  *
43  * Outputs:
44  *     - nothing
45  *
46  * Side Effects:
47  *     - if the heap or dtrees fail to initialize, the program
48  *       will abort.
49  */
init_servers(void)50 void init_servers(void)
51 {
52 	serv_heap = sharedheap_get(sizeof(server_t));
53 	tld_heap = sharedheap_get(sizeof(tld_t));
54 
55 	if (serv_heap == NULL || tld_heap == NULL)
56 	{
57 		slog(LG_INFO, "init_servers(): block allocator failure.");
58 		exit(EXIT_FAILURE);
59 	}
60 
61 	servlist = mowgli_patricia_create(irccasecanon);
62 	sidlist = mowgli_patricia_create(noopcanon);
63 }
64 
65 /*
66  * server_add(const char *name, unsigned int hops, const char *uplink,
67  *            const char *id, const char *desc)
68  *
69  * Server object factory.
70  *
71  * Inputs:
72  *     - name of server object to create or NULL if it's a masked server
73  *     - amount of hops server has from services
74  *     - name of server's uplink or NULL if it's us
75  *     - SID of uplink if applicable otherwise NULL
76  *     - server's description
77  *
78  * Outputs:
79  *     - on success, a new server object
80  *
81  * Side Effects:
82  *     - the new server object is added to the server and sid DTree.
83  */
server_add(const char * name,unsigned int hops,server_t * uplink,const char * id,const char * desc)84 server_t *server_add(const char *name, unsigned int hops, server_t *uplink, const char *id, const char *desc)
85 {
86 	server_t *s;
87 	const char *tld;
88 
89 	/* Masked servers must have a SID */
90 	return_val_if_fail(name != NULL || id != NULL, NULL);
91 	/* Masked servers must be behind something else */
92 	return_val_if_fail(name != NULL || uplink != NULL, NULL);
93 
94 	if (uplink)
95 	{
96 		if (name == NULL)
97 			slog(LG_NETWORK, "server_add(): %s (%s), masked", uplink->name, id);
98 		else if (id != NULL)
99 			slog(LG_NETWORK, "server_add(): %s (%s), uplink %s", name, id, uplink->name);
100 		else
101 			slog(LG_NETWORK, "server_add(): %s, uplink %s", name, uplink->name);
102 	}
103 	else
104 		slog(LG_DEBUG, "server_add(): %s, root", name);
105 
106 	s = mowgli_heap_alloc(serv_heap);
107 
108 	if (id != NULL)
109 	{
110 		s->sid = sstrdup(id);
111 		mowgli_patricia_add(sidlist, s->sid, s);
112 	}
113 
114 	/* check to see if it's hidden */
115 	if (!strncmp(desc, "(H)", 3))
116 	{
117 		s->flags |= SF_HIDE;
118 		desc += 3;
119 		if (*desc == ' ')
120 			desc++;
121 	}
122 
123 	s->name = sstrdup(name != NULL ? name : uplink->name);
124 	s->desc = sstrdup(desc);
125 	s->hops = hops;
126 	s->connected_since = CURRTIME;
127 
128 	if (name != NULL)
129 		mowgli_patricia_add(servlist, s->name, s);
130 	else
131 		s->flags |= SF_MASKED;
132 
133 	if (uplink)
134 	{
135 		s->uplink = uplink;
136 		mowgli_node_add(s, mowgli_node_create(), &uplink->children);
137 	}
138 
139 	/* tld list for global noticer */
140 	tld = strrchr(s->name, '.');
141 
142 	if (tld != NULL)
143 	{
144 		if (!tld_find(tld))
145 			tld_add(tld);
146 	}
147 
148 	cnt.server++;
149 
150 	hook_call_server_add(s);
151 
152 	return s;
153 }
154 
155 /*
156  * server_delete(const char *name)
157  *
158  * Finds and recursively destroys a server object.
159  *
160  * Inputs:
161  *     - name of server to find and destroy
162  *
163  * Outputs:
164  *     - nothing
165  *
166  * Side Effects:
167  *     - all users and servers attached to the target are recursively deleted
168  */
server_delete(const char * name)169 void server_delete(const char *name)
170 {
171 	server_t *s = server_find(name);
172 
173 	if (!s)
174 	{
175 		slog(LG_DEBUG, "server_delete(): called for nonexistant server: %s", name);
176 
177 		return;
178 	}
179 	server_delete_serv(s);
180 }
181 
server_delete_serv(server_t * s)182 static void server_delete_serv(server_t *s)
183 {
184 	server_t *child;
185 	user_t *u;
186 	mowgli_node_t *n, *tn;
187 
188 	if (s == me.me)
189 	{
190 		/* Deleting this would cause confusion, so let's not do it.
191 		 * Some ircds send SQUIT <myname> when atheme is squitted.
192 		 * -- jilles
193 		 */
194 		slog(LG_DEBUG, "server_delete(): tried to delete myself");
195 		return;
196 	}
197 
198 	if (s->sid)
199 		slog(me.connected ? LG_NETWORK : LG_DEBUG, "server_delete(): %s (%s), uplink %s (%d users)",
200 				s->name, s->sid,
201 				s->uplink != NULL ? s->uplink->name : "<none>",
202 				s->users);
203 	else
204 		slog(me.connected ? LG_NETWORK : LG_DEBUG, "server_delete(): %s, uplink %s (%d users)",
205 				s->name, s->uplink != NULL ? s->uplink->name : "<none>",
206 				s->users);
207 
208 	hook_call_server_delete((&(hook_server_delete_t){ .s = s }));
209 
210 	/* first go through it's users and kill all of them */
211 	MOWGLI_ITER_FOREACH_SAFE(n, tn, s->userlist.head)
212 	{
213 		u = (user_t *)n->data;
214 		/* This user split, allow bursted logins for the account.
215 		 * XXX should we do this here?
216 		 * -- jilles */
217 		if (u->myuser != NULL)
218 			u->myuser->flags &= ~MU_NOBURSTLOGIN;
219 		user_delete(u, "*.net *.split");
220 	}
221 
222 	MOWGLI_ITER_FOREACH_SAFE(n, tn, s->children.head)
223 	{
224 		child = n->data;
225 		server_delete_serv(child);
226 	}
227 
228 	/* now remove the server */
229 	if (!(s->flags & SF_MASKED))
230 		mowgli_patricia_delete(servlist, s->name);
231 
232 	if (s->sid)
233 		mowgli_patricia_delete(sidlist, s->sid);
234 
235 	if (s->uplink)
236 	{
237 		n = mowgli_node_find(s, &s->uplink->children);
238 		mowgli_node_delete(n, &s->uplink->children);
239 		mowgli_node_free(n);
240 	}
241 
242 	/* If unconnect semantics SQUIT was confirmed, introduce the jupe
243 	 * now. This must be after removing the server from the dtrees.
244 	 * -- jilles */
245 	if (s->flags & SF_JUPE_PENDING)
246 		jupe(s->name, "Juped");
247 
248 	free(s->name);
249 	free(s->desc);
250 	if (s->sid)
251 		free(s->sid);
252 
253 	mowgli_heap_free(serv_heap, s);
254 
255 	cnt.server--;
256 }
257 
258 /*
259  * server_find(const char *name)
260  *
261  * Finds a server object.
262  *
263  * Inputs:
264  *     - name of server to find
265  *
266  * Outputs:
267  *     - on success, the server object
268  *     - on failure, NULL.
269  *
270  * Side Effects:
271  *     - none
272  */
server_find(const char * name)273 server_t *server_find(const char *name)
274 {
275 	server_t *s;
276 
277 	s = mowgli_patricia_retrieve(sidlist, name);
278 	if (s != NULL)
279 		return s;
280 
281 	return mowgli_patricia_retrieve(servlist, name);
282 }
283 
284 /*
285  * tld_add(const char *name)
286  *
287  * TLD object factory.
288  *
289  * Inputs:
290  *     - name of TLD to cache as an object
291  *
292  * Outputs:
293  *     - on success, a TLD object
294  *     - on failure, NULL
295  *
296  * Side Effects:
297  *     - the TLD object is registered with the TLD list.
298  */
tld_add(const char * name)299 tld_t *tld_add(const char *name)
300 {
301         tld_t *tld;
302         mowgli_node_t *n = mowgli_node_create();
303 
304         slog(LG_DEBUG, "tld_add(): %s", name);
305 
306         tld = mowgli_heap_alloc(tld_heap);
307 
308         mowgli_node_add(tld, n, &tldlist);
309 
310         tld->name = sstrdup(name);
311 
312         cnt.tld++;
313 
314         return tld;
315 }
316 
317 /*
318  * tld_delete(const char *name)
319  *
320  * Destroys a TLD object.
321  *
322  * Inputs:
323  *     - name of TLD object to destroy
324  *
325  * Outputs:
326  *     - nothing
327  *
328  * Side Effects:
329  *     - the TLD object is removed and deregistered from the TLD list.
330  */
tld_delete(const char * name)331 void tld_delete(const char *name)
332 {
333         tld_t *tld = tld_find(name);
334         mowgli_node_t *n;
335 
336         if (!tld)
337         {
338                 slog(LG_DEBUG, "tld_delete(): called for nonexistant tld: %s", name);
339 
340                 return;
341         }
342 
343         slog(LG_DEBUG, "tld_delete(): %s", tld->name);
344 
345         n = mowgli_node_find(tld, &tldlist);
346         mowgli_node_delete(n, &tldlist);
347         mowgli_node_free(n);
348 
349         free(tld->name);
350         mowgli_heap_free(tld_heap, tld);
351 
352         cnt.tld--;
353 }
354 
355 /*
356  * tld_find(const char *name)
357  *
358  * Looks up a TLD object.
359  *
360  * Inputs:
361  *     - name of TLD object to look up
362  *
363  * Outputs:
364  *     - on success, the TLD object
365  *     - on failure, NULL
366  *
367  * Side Effects:
368  *     - none
369  */
tld_find(const char * name)370 tld_t *tld_find(const char *name)
371 {
372         tld_t *tld;
373         mowgli_node_t *n;
374 
375 	if (name == NULL)
376 		return NULL;
377 
378         MOWGLI_ITER_FOREACH(n, tldlist.head)
379         {
380                 tld = (tld_t *)n->data;
381 
382                 if (!strcasecmp(name, tld->name))
383                         return tld;
384         }
385 
386         return NULL;
387 }
388 
389 /* vim:cinoptions=>s,e0,n0,f0,{0,}0,^0,=s,ps,t0,c3,+s,(2s,us,)20,*30,gs,hs
390  * vim:ts=8
391  * vim:sw=8
392  * vim:noexpandtab
393  */
394