]>
Commit | Line | Data |
---|---|---|
3bd189cb JR |
1 | /************************************************************************ |
2 | * IRC - Internet Relay Chat, ircd/s_conf.c | |
3 | * Copyright (C) 1990 Jarkko Oikarinen and | |
4 | * University of Oulu, Computing Center | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 1, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | */ | |
20 | ||
21 | /* -- avalon -- 20 Feb 1992 | |
22 | * Reversed the order of the params for attach_conf(). | |
23 | * detach_conf() and attach_conf() are now the same: | |
24 | * function_conf(aClient *, aConfItem *) | |
25 | */ | |
26 | ||
27 | /* -- Jto -- 20 Jun 1990 | |
28 | * Added gruner's overnight fix.. | |
29 | */ | |
30 | ||
31 | /* -- Jto -- 16 Jun 1990 | |
32 | * Moved matches to ../common/match.c | |
33 | */ | |
34 | ||
35 | /* -- Jto -- 03 Jun 1990 | |
36 | * Added Kill fixes from gruner@lan.informatik.tu-muenchen.de | |
37 | * Added jarlek's msgbase fix (I still don't understand it... -- Jto) | |
38 | */ | |
39 | ||
40 | /* -- Jto -- 13 May 1990 | |
41 | * Added fixes from msa: | |
42 | * Comments and return value to init_conf() | |
43 | */ | |
44 | ||
45 | /* | |
46 | * -- Jto -- 12 May 1990 | |
47 | * Added close() into configuration file (was forgotten...) | |
48 | */ | |
49 | ||
50 | #ifndef lint | |
51 | static const volatile char rcsid[] = "@(#)$Id: s_conf.c,v 1.187 2008/06/24 22:24:52 chopin Exp $"; | |
52 | #endif | |
53 | ||
54 | #include "os.h" | |
55 | #include "s_defines.h" | |
56 | #define S_CONF_C | |
57 | #include "s_externs.h" | |
58 | #undef S_CONF_C | |
59 | #ifdef ENABLE_CIDR_LIMITS | |
60 | #include "patricia_ext.h" | |
61 | #endif | |
62 | ||
63 | #ifdef TIMEDKLINES | |
64 | static int check_time_interval (char *, char *); | |
65 | #endif | |
66 | static int lookup_confhost (aConfItem *); | |
67 | ||
68 | #ifdef CONFIG_DIRECTIVE_INCLUDE | |
69 | #include "config_read.c" | |
70 | #endif | |
71 | ||
72 | aConfItem *conf = NULL; | |
73 | aConfItem *kconf = NULL; | |
74 | char *networkname = NULL; | |
75 | #ifdef TKLINE | |
76 | aConfItem *tkconf = NULL; | |
77 | #endif | |
78 | ||
79 | /* Parse I-lines flags from string. | |
80 | * D - Restricted, if no DNS. | |
81 | * I - Restricted, if no ident. | |
82 | * R - Restricted. | |
83 | * E - Kline exempt. | |
84 | * N - Do not resolve hostnames (show as IP). | |
85 | * F - Fallthrough to next I:line when password not matched | |
86 | */ | |
87 | long iline_flags_parse(char *string) | |
88 | { | |
89 | long tmp = 0; | |
90 | ||
91 | if (!string) | |
92 | { | |
93 | return 0; | |
94 | } | |
95 | ||
96 | if (index(string,'D')) | |
97 | { | |
98 | tmp |= CFLAG_RNODNS; | |
99 | } | |
100 | if (index(string,'I')) | |
101 | { | |
102 | tmp |= CFLAG_RNOIDENT; | |
103 | } | |
104 | if (index(string,'R')) | |
105 | { | |
106 | tmp |= CFLAG_RESTRICTED; | |
107 | } | |
108 | if (index(string,'E')) | |
109 | { | |
110 | tmp |= CFLAG_KEXEMPT; | |
111 | } | |
112 | #ifdef XLINE | |
113 | if (index(string,'e')) | |
114 | { | |
115 | tmp |= CFLAG_XEXEMPT; | |
116 | } | |
117 | #endif | |
118 | if (index(string,'N')) | |
119 | { | |
120 | tmp |= CFLAG_NORESOLVE; | |
121 | } | |
122 | if (index(string,'M')) | |
123 | { | |
124 | tmp |= CFLAG_NORESOLVEMATCH; | |
125 | } | |
126 | if (index(string,'F')) | |
127 | { | |
128 | tmp |= CFLAG_FALL; | |
129 | } | |
130 | ||
131 | return tmp; | |
132 | } | |
133 | ||
134 | /* convert iline flags to human readable string */ | |
135 | char *iline_flags_to_string(long flags) | |
136 | { | |
137 | static char ifsbuf[BUFSIZE]; | |
138 | char *s = ifsbuf; | |
139 | ||
140 | if (flags & CFLAG_RNODNS) | |
141 | { | |
142 | *s++ = 'D'; | |
143 | } | |
144 | if (flags & CFLAG_RNOIDENT) | |
145 | { | |
146 | *s++ = 'I'; | |
147 | } | |
148 | if (flags & CFLAG_RESTRICTED) | |
149 | { | |
150 | *s++ = 'R'; | |
151 | } | |
152 | if (flags & CFLAG_KEXEMPT) | |
153 | { | |
154 | *s++ = 'E'; | |
155 | } | |
156 | #ifdef XLINE | |
157 | if (flags & CFLAG_XEXEMPT) | |
158 | { | |
159 | *s++ = 'e'; | |
160 | } | |
161 | #endif | |
162 | if (flags & CFLAG_NORESOLVE) | |
163 | { | |
164 | *s++ = 'N'; | |
165 | } | |
166 | if (flags & CFLAG_NORESOLVEMATCH) | |
167 | { | |
168 | *s++ = 'M'; | |
169 | } | |
170 | if (flags & CFLAG_FALL) | |
171 | { | |
172 | *s++ = 'F'; | |
173 | } | |
174 | if (s == ifsbuf) | |
175 | { | |
176 | *s++ = '-'; | |
177 | } | |
178 | *s++ = '\0'; | |
179 | ||
180 | return ifsbuf; | |
181 | } | |
182 | /* Convert P-line flags from string | |
183 | * D - delayed port | |
184 | * S - server only port | |
185 | */ | |
186 | long pline_flags_parse(char *string) | |
187 | { | |
188 | long tmp = 0; | |
189 | if (index(string, 'D')) | |
190 | { | |
191 | tmp |= PFLAG_DELAYED; | |
192 | } | |
193 | if (index(string, 'S')) | |
194 | { | |
195 | tmp |= PFLAG_SERVERONLY; | |
196 | } | |
197 | return tmp; | |
198 | } | |
199 | /* Convert P-line flags from integer to string | |
200 | */ | |
201 | char *pline_flags_to_string(long flags) | |
202 | { | |
203 | static char pfsbuf[BUFSIZE]; | |
204 | char *s = pfsbuf; | |
205 | ||
206 | if (flags & PFLAG_DELAYED) | |
207 | { | |
208 | *s++ = 'D'; | |
209 | } | |
210 | ||
211 | if (flags & PFLAG_SERVERONLY) | |
212 | { | |
213 | *s++ = 'S'; | |
214 | } | |
215 | ||
216 | if (s == pfsbuf) | |
217 | { | |
218 | *s++ = '-'; | |
219 | } | |
220 | ||
221 | *s++ = '\0'; | |
222 | return pfsbuf; | |
223 | } | |
224 | ||
225 | /* convert oline flags to human readable string */ | |
226 | char *oline_flags_to_string(long flags) | |
227 | { | |
228 | static char ofsbuf[BUFSIZE]; | |
229 | char *s = ofsbuf; | |
230 | ||
231 | if (flags & ACL_LOCOP) | |
232 | *s++ = 'L'; | |
233 | if (flags & ACL_KILLREMOTE) | |
234 | *s++ = 'K'; | |
235 | else if (flags & ACL_KILLLOCAL) | |
236 | *s++ = 'k'; | |
237 | if (flags & ACL_SQUITREMOTE) | |
238 | *s++ ='S'; | |
239 | else if (flags & ACL_SQUITLOCAL) | |
240 | *s++ ='s'; | |
241 | if (flags & ACL_CONNECTREMOTE) | |
242 | *s++ ='C'; | |
243 | else if (flags & ACL_CONNECTLOCAL) | |
244 | *s++ ='c'; | |
245 | if (flags & ACL_CLOSE) | |
246 | *s++ ='l'; | |
247 | if (flags & ACL_HAZH) | |
248 | *s++ ='h'; | |
249 | if (flags & ACL_DNS) | |
250 | *s++ ='d'; | |
251 | if (flags & ACL_REHASH) | |
252 | *s++ ='r'; | |
253 | if (flags & ACL_RESTART) | |
254 | *s++ ='R'; | |
255 | if (flags & ACL_DIE) | |
256 | *s++ ='D'; | |
257 | if (flags & ACL_SET) | |
258 | *s++ ='e'; | |
259 | if (flags & ACL_TKLINE) | |
260 | *s++ ='T'; | |
261 | if (flags & ACL_KLINE) | |
262 | *s++ ='q'; | |
263 | #ifdef CLIENTS_CHANNEL | |
264 | if (flags & ACL_CLIENTS) | |
265 | *s++ ='&'; | |
266 | #endif | |
267 | if (flags & ACL_NOPENALTY) | |
268 | *s++ = 'P'; | |
269 | if (flags & ACL_CANFLOOD) | |
270 | *s++ = 'p'; | |
271 | if (flags & ACL_TRACE) | |
272 | *s++ = 't'; | |
273 | #ifdef ENABLE_SIDTRACE | |
274 | if (flags & ACL_SIDTRACE) | |
275 | *s++ = 'v'; | |
276 | #endif | |
277 | if (s == ofsbuf) | |
278 | *s++ = '-'; | |
279 | *s++ = '\0'; | |
280 | return ofsbuf; | |
281 | } | |
282 | /* convert string from config to flags */ | |
283 | long oline_flags_parse(char *string) | |
284 | { | |
285 | long tmp = 0; | |
286 | char *s; | |
287 | ||
288 | for (s = string; *s; s++) | |
289 | { | |
290 | switch(*s) | |
291 | { | |
292 | case 'L': tmp |= ACL_LOCOP; break; | |
293 | case 'A': tmp |= (ACL_ALL & | |
294 | ~(ACL_LOCOP|ACL_CLIENTS|ACL_NOPENALTY|ACL_CANFLOOD)); | |
295 | break; | |
296 | case 'K': tmp |= ACL_KILL; break; | |
297 | case 'k': tmp |= ACL_KILLLOCAL; break; | |
298 | case 'S': tmp |= ACL_SQUIT; break; | |
299 | case 's': tmp |= ACL_SQUITLOCAL; break; | |
300 | case 'C': tmp |= ACL_CONNECT; break; | |
301 | case 'c': tmp |= ACL_CONNECTLOCAL; break; | |
302 | case 'l': tmp |= ACL_CLOSE; break; | |
303 | case 'h': tmp |= ACL_HAZH; break; | |
304 | case 'd': tmp |= ACL_DNS; break; | |
305 | case 'r': tmp |= ACL_REHASH; break; | |
306 | case 'R': tmp |= ACL_RESTART; break; | |
307 | case 'D': tmp |= ACL_DIE; break; | |
308 | case 'e': tmp |= ACL_SET; break; | |
309 | case 'T': tmp |= ACL_TKLINE; break; | |
310 | case 'q': tmp |= ACL_KLINE; break; | |
311 | #ifdef CLIENTS_CHANNEL | |
312 | case '&': tmp |= ACL_CLIENTS; break; | |
313 | #endif | |
314 | case 'P': tmp |= ACL_NOPENALTY; break; | |
315 | case 'p': tmp |= ACL_CANFLOOD; break; | |
316 | case 't': tmp |= ACL_TRACE; break; | |
317 | #ifdef ENABLE_SIDTRACE | |
318 | case 'v': tmp |= ACL_SIDTRACE; break; | |
319 | #endif | |
320 | } | |
321 | } | |
322 | if (tmp & ACL_LOCOP) | |
323 | tmp &= ~ACL_ALL_REMOTE; | |
324 | #ifdef OPER_KILL | |
325 | # ifndef OPER_KILL_REMOTE | |
326 | tmp &= ~ACL_KILLREMOTE; | |
327 | # endif | |
328 | #else | |
329 | tmp &= ~ACL_KILL; | |
330 | #endif | |
331 | #ifndef OPER_REHASH | |
332 | tmp &= ~ACL_REHASH; | |
333 | #endif | |
334 | #ifndef OPER_SQUIT | |
335 | tmp &= ~ACL_SQUIT; | |
336 | #endif | |
337 | #ifndef OPER_CONNECT | |
338 | tmp &= ~ACL_CONNECT; | |
339 | #endif | |
340 | #ifndef OPER_RESTART | |
341 | tmp &= ~ACL_RESTART; | |
342 | #endif | |
343 | #ifndef OPER_DIE | |
344 | tmp &= ~ACL_DIE; | |
345 | #endif | |
346 | #ifndef OPER_SET | |
347 | tmp &= ~ACL_SET; | |
348 | #endif | |
349 | #ifndef OPER_TKLINE | |
350 | tmp &= ~ACL_TKLINE; | |
351 | #endif | |
352 | #ifndef OPER_KLINE | |
353 | tmp &= ~ACL_KLINE; | |
354 | #endif | |
355 | return tmp; | |
356 | } | |
357 | /* | |
358 | * remove all conf entries from the client except those which match | |
359 | * the status field mask. | |
360 | */ | |
361 | void det_confs_butmask(aClient *cptr, int mask) | |
362 | { | |
363 | Reg Link *tmp, *tmp2; | |
364 | ||
365 | for (tmp = cptr->confs; tmp; tmp = tmp2) | |
366 | { | |
367 | tmp2 = tmp->next; | |
368 | if ((tmp->value.aconf->status & mask) == 0) | |
369 | (void)detach_conf(cptr, tmp->value.aconf); | |
370 | } | |
371 | } | |
372 | ||
373 | /* | |
374 | * Match address by #IP bitmask (10.11.12.128/27) | |
375 | * Now should work for IPv6 too. | |
376 | * returns -1 on error, 0 on match, 1 when NO match. | |
377 | */ | |
378 | int match_ipmask(char *mask, aClient *cptr, int maskwithusername) | |
379 | { | |
380 | int m; | |
381 | char *p; | |
382 | struct IN_ADDR addr; | |
383 | char dummy[128]; | |
384 | char *omask; | |
385 | u_long lmask; | |
386 | #ifdef INET6 | |
387 | int j; | |
388 | #endif | |
389 | ||
390 | omask = mask; | |
391 | strncpyzt(dummy, mask, sizeof(dummy)); | |
392 | mask = dummy; | |
393 | if (maskwithusername && (p = index(mask, '@'))) | |
394 | { | |
395 | *p = '\0'; | |
396 | if (match(mask, cptr->username)) | |
397 | return 1; | |
398 | mask = p + 1; | |
399 | } | |
400 | if (!(p = index(mask, '/'))) | |
401 | goto badmask; | |
402 | *p = '\0'; | |
403 | ||
404 | if (sscanf(p + 1, "%d", &m) != 1) | |
405 | { | |
406 | goto badmask; | |
407 | } | |
408 | if (!m) | |
409 | return 0; /* x.x.x.x/0 always matches */ | |
410 | #ifndef INET6 | |
411 | if (m < 0 || m > 32) | |
412 | goto badmask; | |
413 | lmask = htonl((u_long)0xffffffffL << (32 - m)); | |
414 | addr.s_addr = inetaddr(mask); | |
415 | return ((addr.s_addr ^ cptr->ip.s_addr) & lmask) ? 1 : 0; | |
416 | #else | |
417 | if (m < 0 || m > 128) | |
418 | goto badmask; | |
419 | ||
420 | if (inetpton(AF_INET6, mask, (void *)addr.s6_addr) != 1) | |
421 | { | |
422 | return -1; | |
423 | } | |
424 | ||
425 | /* Make sure that the ipv4 notation still works. */ | |
426 | if (IN6_IS_ADDR_V4MAPPED(&addr)) | |
427 | { | |
428 | if (m <= 32) | |
429 | m += 96; | |
430 | if (m <= 96) | |
431 | goto badmask; | |
432 | } | |
433 | ||
434 | j = m & 0x1F; /* number not mutliple of 32 bits */ | |
435 | m >>= 5; /* number of 32 bits */ | |
436 | ||
437 | if (m && memcmp((void *)(addr.s6_addr), | |
438 | (void *)(cptr->ip.s6_addr), m << 2)) | |
439 | return 1; | |
440 | ||
441 | if (j) | |
442 | { | |
443 | lmask = htonl((u_long)0xffffffffL << (32 - j)); | |
444 | if ((((u_int32_t *)(addr.s6_addr))[m] ^ | |
445 | ((u_int32_t *)(cptr->ip.s6_addr))[m]) & lmask) | |
446 | return 1; | |
447 | } | |
448 | ||
449 | return 0; | |
450 | #endif | |
451 | badmask: | |
452 | if (maskwithusername) | |
453 | sendto_flag(SCH_ERROR, "Ignoring bad mask: %s", omask); | |
454 | return -1; | |
455 | } | |
456 | ||
457 | /* | |
458 | * find the first (best) I line to attach. | |
459 | */ | |
460 | ||
461 | #define UHConfMatch(x, y, z) (match((x), (index((x), '@') ? (y) : (y)+(z)))) | |
462 | ||
463 | int attach_Iline(aClient *cptr, struct hostent *hp, char *sockhost) | |
464 | { | |
465 | Reg aConfItem *aconf; | |
466 | char uhost[HOSTLEN+USERLEN+2]; | |
467 | char uaddr[HOSTLEN+USERLEN+2]; | |
468 | int ulen = strlen(cptr->username) + 1; /* for '@' */ | |
469 | int retval = -2; /* EXITC_NOILINE in register_user() */ | |
470 | ||
471 | /* We fill uaddr and uhost now, before aconf loop. */ | |
472 | sprintf(uaddr, "%s@%s", cptr->username, sockhost); | |
473 | if (hp) | |
474 | { | |
475 | char fullname[HOSTLEN+1]; | |
476 | ||
477 | /* If not for add_local_domain, I wouldn't need this | |
478 | ** fullname. Can't we add_local_domain somewhere in | |
479 | ** dns code? --B. */ | |
480 | strncpyzt(fullname, hp->h_name, sizeof(fullname)); | |
481 | add_local_domain(fullname, HOSTLEN - strlen(fullname)); | |
482 | Debug((DEBUG_DNS, "a_il: %s->%s", sockhost, fullname)); | |
483 | sprintf(uhost, "%s@%s", cptr->username, fullname); | |
484 | } | |
485 | /* all uses of uhost are guarded by if (hp), so no need to zero it. */ | |
486 | ||
487 | for (aconf = conf; aconf; aconf = aconf->next) | |
488 | { | |
489 | if ((aconf->status != CONF_CLIENT)) | |
490 | { | |
491 | continue; | |
492 | } | |
493 | if (aconf->port && aconf->port != cptr->acpt->port) | |
494 | { | |
495 | continue; | |
496 | } | |
497 | /* aconf->name can be NULL with wrong I:line in the config | |
498 | ** (without all required fields). If aconf->host can be NULL, | |
499 | ** I don't know. Anyway, this is an error! --B. */ | |
500 | if (!aconf->host || !aconf->name) | |
501 | { | |
502 | /* Try another I:line. */ | |
503 | continue; | |
504 | } | |
505 | ||
506 | /* If anything in aconf->name... */ | |
507 | if (*aconf->name) | |
508 | { | |
509 | int namematched = 0; | |
510 | ||
511 | if (hp) | |
512 | { | |
513 | if (!UHConfMatch(aconf->name, uhost, ulen)) | |
514 | { | |
515 | namematched = 1; | |
516 | } | |
517 | } | |
518 | /* Note: here we could do else (!hp) and try to | |
519 | ** check if aconf->name is '*' or '*@*' and | |
520 | ** if so, allow the client. But not doing so | |
521 | ** gives us nice opportunity to distinguish | |
522 | ** between '*' in aconf->name (requires DNS) | |
523 | ** and empty aconf->name (matches any). --B. */ | |
524 | ||
525 | /* Require name to match before checking addr fields. */ | |
526 | if (namematched == 0) | |
527 | { | |
528 | /* Try another I:line. */ | |
529 | continue; | |
530 | } | |
531 | } /* else empty aconf->name, match any hostname. */ | |
532 | ||
533 | if (*aconf->host) | |
534 | { | |
535 | #ifdef UNIXPORT | |
536 | if (IsUnixSocket(cptr) && aconf->host[0] == '/') | |
537 | { | |
538 | if (match(aconf->host, uaddr+ulen)) | |
539 | { | |
540 | /* Try another I:line. */ | |
541 | continue; | |
542 | } | |
543 | } | |
544 | else | |
545 | #endif | |
546 | if (strchr(aconf->host, '/')) /* 1.2.3.0/24 */ | |
547 | { | |
548 | ||
549 | /* match_ipmask takes care of checking | |
550 | ** possible username if aconf->host has '@' */ | |
551 | if (match_ipmask(aconf->host, cptr, 1)) | |
552 | { | |
553 | /* Try another I:line. */ | |
554 | continue; | |
555 | } | |
556 | } | |
557 | else /* 1.2.3.* */ | |
558 | { | |
559 | if (UHConfMatch(aconf->host, uaddr, ulen)) | |
560 | { | |
561 | /* Try another I:line. */ | |
562 | continue; | |
563 | } | |
564 | } | |
565 | } /* else empty aconf->host, match any ipaddr */ | |
566 | ||
567 | /* Password check, if I:line has it. If 'F' flag, try another | |
568 | ** I:line, otherwise bail out and reject client. */ | |
569 | if (!BadPtr(aconf->passwd) && | |
570 | !StrEq(cptr->passwd, aconf->passwd)) | |
571 | { | |
572 | if (IsConfFallThrough(aconf)) | |
573 | { | |
574 | continue; | |
575 | } | |
576 | else | |
577 | { | |
578 | sendto_one(cptr, replies[ERR_PASSWDMISMATCH], | |
579 | ME, BadTo(cptr->name)); | |
580 | retval = -8; /* EXITC_BADPASS */ | |
581 | break; | |
582 | } | |
583 | } | |
584 | ||
585 | /* Various cases of +r. */ | |
586 | if (IsConfRestricted(aconf) || | |
587 | (!hp && IsConfRNoDNS(aconf)) || | |
588 | (!(cptr->flags & FLAGS_GOTID) && IsConfRNoIdent(aconf))) | |
589 | { | |
590 | SetRestricted(cptr); | |
591 | } | |
592 | if (IsConfKlineExempt(aconf)) | |
593 | { | |
594 | SetKlineExempt(cptr); | |
595 | } | |
596 | #ifdef XLINE | |
597 | if (IsConfXlineExempt(aconf)) | |
598 | { | |
599 | ClearXlined(cptr); | |
600 | } | |
601 | #endif | |
602 | ||
603 | /* Copy uhost (hostname) over sockhost, if conf flag permits. */ | |
604 | if (hp && !IsConfNoResolve(aconf)) | |
605 | { | |
606 | get_sockhost(cptr, uhost+ulen); | |
607 | } | |
608 | /* Note that attach_conf() should not return -2. */ | |
609 | if ((retval = attach_conf(cptr, aconf)) < -1) | |
610 | { | |
611 | find_bounce(cptr, ConfClass(aconf), -1); | |
612 | } | |
613 | break; | |
614 | } | |
615 | if (retval == -2) | |
616 | { | |
617 | find_bounce(cptr, 0, -2); | |
618 | } | |
619 | return retval; | |
620 | } | |
621 | ||
622 | /* | |
623 | * Find the single N line and return pointer to it (from list). | |
624 | * If more than one then return NULL pointer. | |
625 | */ | |
626 | aConfItem *count_cnlines(Link *lp) | |
627 | { | |
628 | Reg aConfItem *aconf, *cline = NULL, *nline = NULL; | |
629 | ||
630 | for (; lp; lp = lp->next) | |
631 | { | |
632 | aconf = lp->value.aconf; | |
633 | if (!(aconf->status & CONF_SERVER_MASK)) | |
634 | continue; | |
635 | if ((aconf->status == CONF_CONNECT_SERVER || | |
636 | aconf->status == CONF_ZCONNECT_SERVER) && !cline) | |
637 | cline = aconf; | |
638 | else if (aconf->status == CONF_NOCONNECT_SERVER && !nline) | |
639 | nline = aconf; | |
640 | } | |
641 | return nline; | |
642 | } | |
643 | ||
644 | #ifdef ENABLE_CIDR_LIMITS | |
645 | static int add_cidr_limit(aClient *cptr, aConfItem *aconf) | |
646 | { | |
647 | patricia_node_t *pnode; | |
648 | ||
649 | if(aconf->class->cidr_amount == 0 || aconf->class->cidr_len == 0) | |
650 | return -1; | |
651 | ||
652 | pnode = patricia_match_ip(ConfCidrTree(aconf), &cptr->ip); | |
653 | ||
654 | /* doesnt exist, create and then allow */ | |
655 | if(pnode == NULL) | |
656 | { | |
657 | pnode = patricia_make_and_lookup_ip(ConfCidrTree(aconf), | |
658 | &cptr->ip, | |
659 | aconf->class->cidr_len); | |
660 | ||
661 | if(pnode == NULL) | |
662 | return -1; | |
663 | ||
664 | pnode->data++; | |
665 | return 1; | |
666 | } | |
667 | ||
668 | if((long)pnode->data >= aconf->class->cidr_amount) | |
669 | return 0; | |
670 | ||
671 | pnode->data++; | |
672 | return 1; | |
673 | } | |
674 | ||
675 | static void remove_cidr_limit(aClient *cptr, aConfItem *aconf) | |
676 | { | |
677 | patricia_node_t *pnode; | |
678 | ||
679 | if(ConfMaxCidrAmount(aconf) == 0 || ConfCidrLen(aconf) == 0) | |
680 | return; | |
681 | ||
682 | pnode = patricia_match_ip(ConfCidrTree(aconf), &cptr->ip); | |
683 | ||
684 | if(pnode == NULL) | |
685 | return; | |
686 | ||
687 | pnode->data--; | |
688 | ||
689 | if(((unsigned long) pnode->data) == 0) | |
690 | patricia_remove(ConfCidrTree(aconf), pnode); | |
691 | } | |
692 | #endif /* ENABLE_CIDR_LIMITS */ | |
693 | ||
694 | /* | |
695 | ** detach_conf | |
696 | ** Disassociate configuration from the client. | |
697 | ** Also removes a class from the list if marked for deleting. | |
698 | */ | |
699 | int detach_conf(aClient *cptr, aConfItem *aconf) | |
700 | { | |
701 | Reg Link **lp, *tmp; | |
702 | aConfItem **aconf2,*aconf3; | |
703 | ||
704 | lp = &(cptr->confs); | |
705 | ||
706 | while (*lp) | |
707 | { | |
708 | if ((*lp)->value.aconf == aconf) | |
709 | { | |
710 | if ((aconf) && (Class(aconf))) | |
711 | { | |
712 | if (aconf->status & CONF_CLIENT_MASK) | |
713 | { | |
714 | if (ConfLinks(aconf) > 0) | |
715 | --ConfLinks(aconf); | |
716 | #ifdef ENABLE_CIDR_LIMITS | |
717 | if ((aconf->status & CONF_CLIENT)) | |
718 | remove_cidr_limit(cptr, aconf); | |
719 | #endif | |
720 | } | |
721 | ||
722 | if (ConfMaxLinks(aconf) == -1 && | |
723 | ConfLinks(aconf) == 0) | |
724 | { | |
725 | free_class(Class(aconf)); | |
726 | Class(aconf) = NULL; | |
727 | } | |
728 | } | |
729 | if (aconf && --aconf->clients <= 0 && IsIllegal(aconf)) | |
730 | { | |
731 | /* Remove the conf entry from the Conf linked list */ | |
732 | for (aconf2 = &conf; (aconf3 = *aconf2); ) | |
733 | { | |
734 | if (aconf3 == aconf) | |
735 | { | |
736 | *aconf2 = aconf3->next; | |
737 | aconf3->next = NULL; | |
738 | free_conf(aconf); | |
739 | } | |
740 | else | |
741 | { | |
742 | aconf2 = &aconf3->next; | |
743 | } | |
744 | } | |
745 | } | |
746 | tmp = *lp; | |
747 | *lp = tmp->next; | |
748 | free_link(tmp); | |
749 | istat.is_conflink--; | |
750 | return 0; | |
751 | } | |
752 | else | |
753 | lp = &((*lp)->next); | |
754 | } | |
755 | return -1; | |
756 | } | |
757 | ||
758 | static int is_attached(aConfItem *aconf, aClient *cptr) | |
759 | { | |
760 | Reg Link *lp; | |
761 | ||
762 | for (lp = cptr->confs; lp; lp = lp->next) | |
763 | if (lp->value.aconf == aconf) | |
764 | break; | |
765 | ||
766 | return (lp) ? 1 : 0; | |
767 | } | |
768 | ||
769 | /* | |
770 | ** attach_conf | |
771 | ** Associate a specific configuration entry to a *local* | |
772 | ** client (this is the one which used in accepting the | |
773 | ** connection). Note, that this automaticly changes the | |
774 | ** attachment if there was an old one... | |
775 | ** Non-zero return value is used in register_user(). | |
776 | */ | |
777 | int attach_conf(aClient *cptr, aConfItem *aconf) | |
778 | { | |
779 | Reg Link *lp; | |
780 | ||
781 | if (is_attached(aconf, cptr)) | |
782 | return 1; | |
783 | if (IsIllegal(aconf)) | |
784 | return -1; /* EXITC_FAILURE, hmm */ | |
785 | if ((aconf->status & (CONF_OPERATOR | CONF_CLIENT ))) | |
786 | { | |
787 | if ( | |
788 | #ifdef YLINE_LIMITS_OLD_BEHAVIOUR | |
789 | aconf->clients >= ConfMaxLinks(aconf) | |
790 | #else | |
791 | ConfLinks(aconf) >= ConfMaxLinks(aconf) | |
792 | #endif | |
793 | && ConfMaxLinks(aconf) > 0) | |
794 | return -3; /* EXITC_YLINEMAX */ | |
795 | } | |
796 | ||
797 | if ((aconf->status & CONF_CLIENT)) | |
798 | { | |
799 | int hcnt = 0, ucnt = 0; | |
800 | int ghcnt = 0, gucnt = 0; | |
801 | anUser *user = NULL; | |
802 | /* check on local/global limits per host and per user@host */ | |
803 | ||
804 | #ifdef ENABLE_CIDR_LIMITS | |
805 | if(!add_cidr_limit(cptr, aconf)) | |
806 | return -4; /* EXITC_LHMAX */ | |
807 | #endif | |
808 | /* | |
809 | ** local limits first to save CPU if any is hit. | |
810 | ** host check is done on the IP address. | |
811 | ** user check is done on the IDENT reply. | |
812 | */ | |
813 | if (ConfMaxHLocal(aconf) > 0 || ConfMaxUHLocal(aconf) > 0 || | |
814 | ConfMaxHGlobal(aconf) > 0 || ConfMaxUHGlobal(aconf) > 0 ) | |
815 | { | |
816 | #ifdef YLINE_LIMITS_IPHASH | |
817 | for ((user = hash_find_ip(cptr->user->sip, NULL)); | |
818 | user; user = user->iphnext) | |
819 | if (!mycmp(cptr->user->sip, user->sip)) | |
820 | #else | |
821 | for ((user = hash_find_hostname(cptr->sockhost, NULL)); | |
822 | user; user = user->hhnext) | |
823 | if (!mycmp(cptr->sockhost, user->host)) | |
824 | #endif | |
825 | { | |
826 | ghcnt++; | |
827 | if (ConfMaxHGlobal(aconf) > 0 && | |
828 | ghcnt >= ConfMaxHGlobal(aconf)) | |
829 | { | |
830 | return -6; /* EXITC_GHMAX */ | |
831 | } | |
832 | if (MyConnect(user->bcptr)) | |
833 | { | |
834 | hcnt++; | |
835 | if (ConfMaxHLocal(aconf) > 0 && | |
836 | hcnt >= ConfMaxHLocal(aconf)) | |
837 | { | |
838 | return -4; /* EXITC_LHMAX */ | |
839 | } | |
840 | if (!mycmp(user->bcptr->auth, | |
841 | cptr->auth)) | |
842 | { | |
843 | ucnt++; | |
844 | gucnt++; | |
845 | } | |
846 | } | |
847 | else | |
848 | { | |
849 | if (!mycmp(user->username, | |
850 | cptr->user->username | |
851 | )) | |
852 | { | |
853 | gucnt++; | |
854 | } | |
855 | } | |
856 | if (ConfMaxUHLocal(aconf) > 0 && | |
857 | ucnt >= ConfMaxUHLocal(aconf)) | |
858 | { | |
859 | return -5; /* EXITC_LUHMAX */ | |
860 | } | |
861 | ||
862 | if (ConfMaxUHGlobal(aconf) > 0 && | |
863 | gucnt >= ConfMaxUHGlobal(aconf)) | |
864 | { | |
865 | return -7; /* EXITC_GUHMAX */ | |
866 | } | |
867 | } | |
868 | } | |
869 | } | |
870 | ||
871 | ||
872 | lp = make_link(); | |
873 | istat.is_conflink++; | |
874 | lp->next = cptr->confs; | |
875 | lp->value.aconf = aconf; | |
876 | cptr->confs = lp; | |
877 | cptr->ping = get_client_ping(cptr); | |
878 | aconf->clients++; | |
879 | if (aconf->status & CONF_CLIENT_MASK) | |
880 | ConfLinks(aconf)++; | |
881 | return 0; | |
882 | } | |
883 | ||
884 | ||
885 | aConfItem *find_admin(void) | |
886 | { | |
887 | Reg aConfItem *aconf; | |
888 | ||
889 | for (aconf = conf; aconf; aconf = aconf->next) | |
890 | if (aconf->status & CONF_ADMIN) | |
891 | break; | |
892 | ||
893 | return (aconf); | |
894 | } | |
895 | ||
896 | aConfItem *find_me(void) | |
897 | { | |
898 | Reg aConfItem *aconf; | |
899 | for (aconf = conf; aconf; aconf = aconf->next) | |
900 | if (aconf->status & CONF_ME) | |
901 | break; | |
902 | ||
903 | return (aconf); | |
904 | } | |
905 | ||
906 | /* | |
907 | * attach_confs | |
908 | * Attach a CONF line to a client if the name passed matches that for | |
909 | * the conf file (for non-C/N lines) or is an exact match (C/N lines | |
910 | * only). The difference in behaviour is to stop C:*::* and N:*::*. | |
911 | */ | |
912 | aConfItem *attach_confs(aClient *cptr, char *name, int statmask) | |
913 | { | |
914 | Reg aConfItem *tmp; | |
915 | aConfItem *first = NULL; | |
916 | int len = strlen(name); | |
917 | ||
918 | if (!name || len > HOSTLEN) | |
919 | return NULL; | |
920 | for (tmp = conf; tmp; tmp = tmp->next) | |
921 | { | |
922 | if ((tmp->status & statmask) && !IsIllegal(tmp) && | |
923 | ((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) == 0) && | |
924 | tmp->name && !match(tmp->name, name)) | |
925 | { | |
926 | if (!attach_conf(cptr, tmp) && !first) | |
927 | first = tmp; | |
928 | } | |
929 | else if ((tmp->status & statmask) && !IsIllegal(tmp) && | |
930 | (tmp->status & (CONF_SERVER_MASK|CONF_HUB)) && | |
931 | tmp->name && !mycmp(tmp->name, name)) | |
932 | { | |
933 | if (!attach_conf(cptr, tmp) && !first) | |
934 | first = tmp; | |
935 | } | |
936 | } | |
937 | return (first); | |
938 | } | |
939 | ||
940 | /* | |
941 | * Added for new access check meLazy | |
942 | */ | |
943 | aConfItem *attach_confs_host(aClient *cptr, char *host, int statmask) | |
944 | { | |
945 | Reg aConfItem *tmp; | |
946 | aConfItem *first = NULL; | |
947 | int len = strlen(host); | |
948 | ||
949 | if (!host || len > HOSTLEN) | |
950 | return NULL; | |
951 | ||
952 | for (tmp = conf; tmp; tmp = tmp->next) | |
953 | { | |
954 | if ((tmp->status & statmask) && !IsIllegal(tmp) && | |
955 | (tmp->status & CONF_SERVER_MASK) == 0 && | |
956 | (!tmp->host || match(tmp->host, host) == 0)) | |
957 | { | |
958 | if (!attach_conf(cptr, tmp) && !first) | |
959 | first = tmp; | |
960 | } | |
961 | else if ((tmp->status & statmask) && !IsIllegal(tmp) && | |
962 | (tmp->status & CONF_SERVER_MASK) && | |
963 | (tmp->host && mycmp(tmp->host, host) == 0)) | |
964 | { | |
965 | if (!attach_conf(cptr, tmp) && !first) | |
966 | first = tmp; | |
967 | } | |
968 | } | |
969 | return (first); | |
970 | } | |
971 | ||
972 | /* | |
973 | * find a conf entry which matches the hostname and has the same name. | |
974 | */ | |
975 | aConfItem *find_conf_exact(char *name, char *user, char *host, | |
976 | int statmask) | |
977 | { | |
978 | Reg aConfItem *tmp; | |
979 | char userhost[USERLEN+HOSTLEN+3]; | |
980 | ||
981 | sprintf(userhost, "%s@%s", user, host); | |
982 | ||
983 | for (tmp = conf; tmp; tmp = tmp->next) | |
984 | { | |
985 | if (!(tmp->status & statmask) || !tmp->name || !tmp->host || | |
986 | mycmp(tmp->name, name)) | |
987 | continue; | |
988 | /* | |
989 | ** Accept if the *real* hostname (usually sockecthost) | |
990 | ** socket host) matches *either* host or name field | |
991 | ** of the configuration. | |
992 | */ | |
993 | if (match(tmp->host, userhost)) | |
994 | continue; | |
995 | if (tmp->status & CONF_OPERATOR) | |
996 | { | |
997 | if (tmp->clients < MaxLinks(Class(tmp))) | |
998 | return tmp; | |
999 | else | |
1000 | continue; | |
1001 | } | |
1002 | else | |
1003 | return tmp; | |
1004 | } | |
1005 | return NULL; | |
1006 | } | |
1007 | ||
1008 | /* | |
1009 | * find an O-line which matches the hostname and has the same "name". | |
1010 | */ | |
1011 | aConfItem *find_Oline(char *name, aClient *cptr) | |
1012 | { | |
1013 | Reg aConfItem *tmp; | |
1014 | char userhost[USERLEN+HOSTLEN+3]; | |
1015 | char userip[USERLEN+HOSTLEN+3]; | |
1016 | ||
1017 | sprintf(userhost, "%s@%s", cptr->username, cptr->sockhost); | |
1018 | sprintf(userip, "%s@%s", cptr->username, | |
1019 | #ifdef INET6 | |
1020 | (char *)inetntop(AF_INET6, (char *)&cptr->ip, ipv6string, | |
1021 | sizeof(ipv6string)) | |
1022 | #else | |
1023 | (char *)inetntoa((char *)&cptr->ip) | |
1024 | #endif | |
1025 | ); | |
1026 | ||
1027 | ||
1028 | for (tmp = conf; tmp; tmp = tmp->next) | |
1029 | { | |
1030 | if (!(tmp->status & (CONF_OPS)) || !tmp->name || !tmp->host || | |
1031 | mycmp(tmp->name, name)) | |
1032 | continue; | |
1033 | /* | |
1034 | ** Accept if the *real* hostname matches the host field or | |
1035 | ** the ip does. | |
1036 | */ | |
1037 | if (match(tmp->host, userhost) && match(tmp->host, userip) && | |
1038 | (!strchr(tmp->host, '/') | |
1039 | || match_ipmask(tmp->host, cptr, 1))) | |
1040 | continue; | |
1041 | if (tmp->clients < MaxLinks(Class(tmp))) | |
1042 | return tmp; | |
1043 | } | |
1044 | return NULL; | |
1045 | } | |
1046 | ||
1047 | ||
1048 | aConfItem *find_conf_name(char *name, int statmask) | |
1049 | { | |
1050 | Reg aConfItem *tmp; | |
1051 | ||
1052 | for (tmp = conf; tmp; tmp = tmp->next) | |
1053 | { | |
1054 | /* | |
1055 | ** Accept if the hostname matches name field | |
1056 | ** of the configuration. | |
1057 | */ | |
1058 | if ((tmp->status & statmask) && | |
1059 | (!tmp->name || match(tmp->name, name) == 0)) | |
1060 | return tmp; | |
1061 | } | |
1062 | return NULL; | |
1063 | } | |
1064 | ||
1065 | aConfItem *find_conf(Link *lp, char *name, int statmask) | |
1066 | { | |
1067 | Reg aConfItem *tmp; | |
1068 | int namelen = name ? strlen(name) : 0; | |
1069 | ||
1070 | if (namelen > HOSTLEN) | |
1071 | return (aConfItem *) 0; | |
1072 | ||
1073 | for (; lp; lp = lp->next) | |
1074 | { | |
1075 | tmp = lp->value.aconf; | |
1076 | if ((tmp->status & statmask) && | |
1077 | (((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) && | |
1078 | tmp->name && !mycmp(tmp->name, name)) || | |
1079 | ((tmp->status & (CONF_SERVER_MASK|CONF_HUB)) == 0 && | |
1080 | tmp->name && !match(tmp->name, name)))) | |
1081 | return tmp; | |
1082 | } | |
1083 | return NULL; | |
1084 | } | |
1085 | ||
1086 | /* | |
1087 | * Added for new access check meLazy | |
1088 | */ | |
1089 | aConfItem *find_conf_host(Link *lp, char *host, int statmask) | |
1090 | { | |
1091 | Reg aConfItem *tmp; | |
1092 | int hostlen = host ? strlen(host) : 0; | |
1093 | ||
1094 | if (hostlen > HOSTLEN || BadPtr(host)) | |
1095 | return (aConfItem *)NULL; | |
1096 | for (; lp; lp = lp->next) | |
1097 | { | |
1098 | tmp = lp->value.aconf; | |
1099 | if (tmp->status & statmask && | |
1100 | (!(tmp->status & CONF_SERVER_MASK || tmp->host) || | |
1101 | (tmp->host && !match(tmp->host, host)))) | |
1102 | return tmp; | |
1103 | } | |
1104 | return NULL; | |
1105 | } | |
1106 | ||
1107 | aConfItem *find_conf_host_sid(Link *lp, char *host, char *sid, int statmask) | |
1108 | { | |
1109 | Reg aConfItem *tmp; | |
1110 | int hostlen = host ? strlen(host) : 0; | |
1111 | ||
1112 | if (hostlen > HOSTLEN || BadPtr(host)) | |
1113 | return (aConfItem *)NULL; | |
1114 | for (; lp; lp = lp->next) | |
1115 | { | |
1116 | tmp = lp->value.aconf; | |
1117 | if (tmp->status & statmask && | |
1118 | (!(tmp->status & CONF_SERVER_MASK || tmp->host) || | |
1119 | (tmp->host && !match(tmp->host, host))) && | |
1120 | (!tmp->passwd || !tmp->passwd[0] || | |
1121 | !match(tmp->passwd, sid)) ) | |
1122 | { | |
1123 | return tmp; | |
1124 | } | |
1125 | } | |
1126 | return NULL; | |
1127 | } | |
1128 | ||
1129 | /* | |
1130 | * find_conf_ip | |
1131 | * | |
1132 | * Find a conf line using the IP# stored in it to search upon. | |
1133 | * Added 1/8/92 by Avalon. | |
1134 | */ | |
1135 | aConfItem *find_conf_ip(Link *lp, char *ip, char *user, int statmask) | |
1136 | { | |
1137 | Reg aConfItem *tmp; | |
1138 | Reg char *s; | |
1139 | ||
1140 | for (; lp; lp = lp->next) | |
1141 | { | |
1142 | tmp = lp->value.aconf; | |
1143 | if (!(tmp->status & statmask)) | |
1144 | continue; | |
1145 | s = index(tmp->host, '@'); | |
1146 | *s = '\0'; | |
1147 | if (match(tmp->host, user)) | |
1148 | { | |
1149 | *s = '@'; | |
1150 | continue; | |
1151 | } | |
1152 | *s = '@'; | |
1153 | if (!bcmp((char *)&tmp->ipnum, ip, sizeof(struct IN_ADDR))) | |
1154 | return tmp; | |
1155 | } | |
1156 | return NULL; | |
1157 | } | |
1158 | ||
1159 | /* | |
1160 | * find_conf_entry | |
1161 | * | |
1162 | * - looks for a match on all given fields. | |
1163 | */ | |
1164 | aConfItem *find_conf_entry(aConfItem *aconf, u_int mask) | |
1165 | { | |
1166 | Reg aConfItem *bconf; | |
1167 | ||
1168 | for (bconf = conf, mask &= ~CONF_ILLEGAL; bconf; bconf = bconf->next) | |
1169 | { | |
1170 | if (!(bconf->status & mask) || (bconf->port != aconf->port)) | |
1171 | continue; | |
1172 | ||
1173 | if ((BadPtr(bconf->host) && !BadPtr(aconf->host)) || | |
1174 | (BadPtr(aconf->host) && !BadPtr(bconf->host))) | |
1175 | continue; | |
1176 | if (!BadPtr(bconf->host) && mycmp(bconf->host, aconf->host)) | |
1177 | continue; | |
1178 | ||
1179 | if ((BadPtr(bconf->passwd) && !BadPtr(aconf->passwd)) || | |
1180 | (BadPtr(aconf->passwd) && !BadPtr(bconf->passwd))) | |
1181 | continue; | |
1182 | if (!BadPtr(bconf->passwd) && | |
1183 | mycmp(bconf->passwd, aconf->passwd)) | |
1184 | continue; | |
1185 | ||
1186 | if ((BadPtr(bconf->name) && !BadPtr(aconf->name)) || | |
1187 | (BadPtr(aconf->name) && !BadPtr(bconf->name))) | |
1188 | continue; | |
1189 | if (!BadPtr(bconf->name) && mycmp(bconf->name, aconf->name)) | |
1190 | continue; | |
1191 | break; | |
1192 | } | |
1193 | return bconf; | |
1194 | } | |
1195 | ||
1196 | /* | |
1197 | * rehash | |
1198 | * | |
1199 | * Actual REHASH service routine. Called with sig == 0 if it has been called | |
1200 | * as a result of an operator issuing this command, else assume it has been | |
1201 | * called as a result of the server receiving a HUP signal. | |
1202 | */ | |
1203 | int rehash(aClient *cptr, aClient *sptr, int sig) | |
1204 | { | |
1205 | Reg aConfItem **tmp = &conf, *tmp2 = NULL; | |
1206 | Reg aClass *cltmp; | |
1207 | Reg aClient *acptr; | |
1208 | Reg int i; | |
1209 | int ret = 0; | |
1210 | ||
1211 | if (sig == 1) | |
1212 | { | |
1213 | sendto_flag(SCH_NOTICE, | |
1214 | "Got signal SIGHUP, reloading ircd.conf file"); | |
1215 | logfiles_close(); | |
1216 | logfiles_open(); | |
1217 | #ifdef ULTRIX | |
1218 | if (fork() > 0) | |
1219 | exit(0); | |
1220 | write_pidfile(); | |
1221 | #endif | |
1222 | } | |
1223 | ||
1224 | for (i = 0; i <= highest_fd; i++) | |
1225 | if ((acptr = local[i]) && !IsMe(acptr)) | |
1226 | { | |
1227 | /* | |
1228 | * Nullify any references from client structures to | |
1229 | * this host structure which is about to be freed. | |
1230 | * Could always keep reference counts instead of | |
1231 | * this....-avalon | |
1232 | */ | |
1233 | acptr->hostp = NULL; | |
1234 | } | |
1235 | ||
1236 | while ((tmp2 = *tmp)) | |
1237 | if (tmp2->clients || tmp2->status & CONF_LISTEN_PORT) | |
1238 | { | |
1239 | /* | |
1240 | ** Configuration entry is still in use by some | |
1241 | ** local clients, cannot delete it--mark it so | |
1242 | ** that it will be deleted when the last client | |
1243 | ** exits... | |
1244 | */ | |
1245 | if (!(tmp2->status & (CONF_LISTEN_PORT|CONF_CLIENT))) | |
1246 | { | |
1247 | *tmp = tmp2->next; | |
1248 | tmp2->next = NULL; | |
1249 | } | |
1250 | else | |
1251 | tmp = &tmp2->next; | |
1252 | tmp2->status |= CONF_ILLEGAL; | |
1253 | } | |
1254 | else | |
1255 | { | |
1256 | *tmp = tmp2->next; | |
1257 | free_conf(tmp2); | |
1258 | } | |
1259 | ||
1260 | tmp = &kconf; | |
1261 | while ((tmp2 = *tmp)) | |
1262 | { | |
1263 | *tmp = tmp2->next; | |
1264 | free_conf(tmp2); | |
1265 | } | |
1266 | ||
1267 | /* | |
1268 | * We don't delete the class table, rather mark all entries | |
1269 | * for deletion. The table is cleaned up by check_class. - avalon | |
1270 | */ | |
1271 | for (cltmp = NextClass(FirstClass()); cltmp; cltmp = NextClass(cltmp)) | |
1272 | MaxLinks(cltmp) = -1; | |
1273 | ||
1274 | if (sig == 'a') | |
1275 | start_iauth(2); /* 2 means kill iauth first */ | |
1276 | if (sig == 'd') | |
1277 | { | |
1278 | flush_cache(); | |
1279 | close(resfd); | |
1280 | resfd = init_resolver(0x1f); | |
1281 | } | |
1282 | #ifdef TKLINE | |
1283 | if (sig == 't') | |
1284 | tkline_expire(1); | |
1285 | #endif | |
1286 | (void) initconf(0); | |
1287 | close_listeners(); | |
1288 | reopen_listeners(); | |
1289 | ||
1290 | /* | |
1291 | * Flush *unused* config entries. | |
1292 | */ | |
1293 | for (tmp = &conf; (tmp2 = *tmp); ) | |
1294 | if (!(tmp2->status & CONF_ILLEGAL) || (tmp2->clients > 0)) | |
1295 | tmp = &tmp2->next; | |
1296 | else | |
1297 | { | |
1298 | *tmp = tmp2->next; | |
1299 | tmp2->next = NULL; | |
1300 | free_conf(tmp2); | |
1301 | } | |
1302 | ||
1303 | read_motd(IRCDMOTD_PATH); | |
1304 | if (rehashed == 1) | |
1305 | { | |
1306 | /* queue another rehash for later */ | |
1307 | rehashed = 2; | |
1308 | } | |
1309 | else if (rehashed == 0) | |
1310 | { | |
1311 | rehashed = 1; | |
1312 | } | |
1313 | mysrand(timeofday); | |
1314 | return ret; | |
1315 | } | |
1316 | ||
1317 | /* | |
1318 | * openconf | |
1319 | * | |
1320 | * returns -1 on any error or else the fd opened from which to read the | |
1321 | * configuration file from. This may either be the file direct or one end | |
1322 | * of a pipe from m4. | |
1323 | */ | |
1324 | int openconf(void) | |
1325 | { | |
1326 | #ifdef M4_PREPROC | |
1327 | int pi[2], i; | |
1328 | # ifdef HAVE_GNU_M4 | |
1329 | char *includedir, *includedirptr; | |
1330 | ||
1331 | includedir = strdup(IRCDM4_PATH); | |
1332 | includedirptr = strrchr(includedir, '/'); | |
1333 | if (includedirptr) | |
1334 | *includedirptr = '\0'; | |
1335 | # endif | |
1336 | #else | |
1337 | int ret; | |
1338 | #endif | |
1339 | ||
1340 | #ifdef M4_PREPROC | |
1341 | if (pipe(pi) == -1) | |
1342 | return -1; | |
1343 | switch(vfork()) | |
1344 | { | |
1345 | case -1 : | |
1346 | if (serverbooting) | |
1347 | { | |
1348 | fprintf(stderr, | |
1349 | "Fatal Error: Unable to fork() m4 (%s)", | |
1350 | strerror(errno)); | |
1351 | } | |
1352 | return -1; | |
1353 | case 0 : | |
1354 | (void)close(pi[0]); | |
1355 | if (pi[1] != 1) | |
1356 | { | |
1357 | (void)dup2(pi[1], 1); | |
1358 | (void)close(pi[1]); | |
1359 | } | |
1360 | /* If the server is booting, stderr is still open and | |
1361 | * user should receive error message */ | |
1362 | if (!serverbooting) | |
1363 | { | |
1364 | (void)dup2(1,2); | |
1365 | } | |
1366 | for (i = 3; i < MAXCONNECTIONS; i++) | |
1367 | if (local[i]) | |
1368 | (void) close(i); | |
1369 | /* | |
1370 | * m4 maybe anywhere, use execvp to find it. Any error | |
1371 | * goes out with report_error. Could be dangerous, | |
1372 | * two servers running with the same fd's >:-) -avalon | |
1373 | */ | |
1374 | (void)execlp(M4_PATH, "m4", | |
1375 | #ifdef HAVE_GNU_M4 | |
1376 | #ifdef USE_M4_PREFIXES | |
1377 | "-P", | |
1378 | #endif | |
1379 | "-I", includedir, | |
1380 | #endif | |
1381 | #ifdef INET6 | |
1382 | "-DINET6", | |
1383 | #endif | |
1384 | IRCDM4_PATH, configfile, 0); | |
1385 | if (serverbooting) | |
1386 | { | |
1387 | fprintf(stderr,"Fatal Error: Error executing m4 (%s)", | |
1388 | strerror(errno)); | |
1389 | } | |
1390 | report_error("Error executing m4 %s:%s", &me); | |
1391 | _exit(-1); | |
1392 | default : | |
1393 | (void)close(pi[1]); | |
1394 | return pi[0]; | |
1395 | } | |
1396 | #else | |
1397 | if ((ret = open(configfile, O_RDONLY)) == -1) | |
1398 | { | |
1399 | if (serverbooting) | |
1400 | { | |
1401 | fprintf(stderr, | |
1402 | "Fatal Error: Can not open configuration file %s (%s)\n", | |
1403 | configfile,strerror(errno)); | |
1404 | } | |
1405 | } | |
1406 | return ret; | |
1407 | #endif | |
1408 | } | |
1409 | ||
1410 | /* | |
1411 | ** char *ipv6_convert(char *orig) | |
1412 | ** converts the original ip address to an standard form | |
1413 | ** returns a pointer to a string. | |
1414 | */ | |
1415 | ||
1416 | #ifdef INET6 | |
1417 | char *ipv6_convert(char *orig) | |
1418 | { | |
1419 | char *s, *t, *buf = NULL; | |
1420 | int i, j; | |
1421 | int len = 1; /* for the '\0' in case of no @ */ | |
1422 | struct in6_addr addr; | |
1423 | ||
1424 | if ((s = strchr(orig, '@'))) | |
1425 | { | |
1426 | *s = '\0'; | |
1427 | len = strlen(orig) + 2; /* +2 for '@' and '\0' */ | |
1428 | buf = (char *)MyMalloc(len); | |
1429 | (void *)strcpy(buf, orig); | |
1430 | buf[len - 2] = '@'; | |
1431 | buf[len - 1] = '\0'; | |
1432 | *s = '@'; | |
1433 | orig = s + 1; | |
1434 | } | |
1435 | ||
1436 | if ((s = strchr(orig, '/'))) | |
1437 | { | |
1438 | *s = '\0'; | |
1439 | s++; | |
1440 | } | |
1441 | ||
1442 | i = inetpton(AF_INET6, orig, addr.s6_addr); | |
1443 | ||
1444 | if (i > 0) | |
1445 | { | |
1446 | t = inetntop(AF_INET6, addr.s6_addr, ipv6string, sizeof(ipv6string)); | |
1447 | } | |
1448 | ||
1449 | j = len - 1; | |
1450 | if (!((i > 0) && t)) | |
1451 | t = orig; | |
1452 | ||
1453 | len += strlen(t); | |
1454 | buf = (char *)MyRealloc(buf, len); | |
1455 | strcpy(buf + j, t); | |
1456 | ||
1457 | if (s) | |
1458 | { | |
1459 | *(s-1) = '/'; /* put the '/' back, not sure it's needed tho */ | |
1460 | j = len; | |
1461 | len += strlen(s) + 1; | |
1462 | buf = (char *)MyRealloc(buf, len); | |
1463 | buf[j - 1] = '/'; | |
1464 | strcpy(buf + j, s); | |
1465 | } | |
1466 | ||
1467 | return buf; | |
1468 | } | |
1469 | #endif | |
1470 | ||
1471 | /* | |
1472 | ** initconf() | |
1473 | ** Read configuration file. | |
1474 | ** | |
1475 | ** returns -1, if file cannot be opened | |
1476 | ** 0, if file opened | |
1477 | */ | |
1478 | ||
1479 | #define MAXCONFLINKS 150 | |
1480 | ||
1481 | int initconf(int opt) | |
1482 | { | |
1483 | static char quotes[9][2] = {{'b', '\b'}, {'f', '\f'}, {'n', '\n'}, | |
1484 | {'r', '\r'}, {'t', '\t'}, {'v', '\v'}, | |
1485 | {'\\', '\\'}, { 0, 0}}; | |
1486 | Reg char *tmp, *s; | |
1487 | int fd, i; | |
1488 | char *tmp2 = NULL, *tmp3 = NULL, *tmp4 = NULL; | |
1489 | #ifdef ENABLE_CIDR_LIMITS | |
1490 | char *tmp5 = NULL; | |
1491 | #endif | |
1492 | int ccount = 0, ncount = 0; | |
1493 | aConfItem *aconf = NULL; | |
1494 | #if defined(CONFIG_DIRECTIVE_INCLUDE) | |
1495 | char *line; | |
1496 | aConfig *ConfigTop, *p; | |
1497 | FILE *fdn; | |
1498 | #else | |
1499 | char line[512], c[80]; | |
1500 | #endif | |
1501 | ||
1502 | Debug((DEBUG_DEBUG, "initconf(): ircd.conf = %s", configfile)); | |
1503 | if ((fd = openconf()) == -1) | |
1504 | { | |
1505 | #if defined(M4_PREPROC) && !defined(USE_IAUTH) | |
1506 | (void)wait(0); | |
1507 | #endif | |
1508 | return -1; | |
1509 | } | |
1510 | #if defined(CONFIG_DIRECTIVE_INCLUDE) | |
1511 | fdn = fdopen(fd, "r"); | |
1512 | if (fdn == NULL) | |
1513 | { | |
1514 | if (serverbooting) | |
1515 | { | |
1516 | fprintf(stderr, | |
1517 | "Fatal Error: Can not open configuration file %s (%s)\n", | |
1518 | configfile,strerror(errno)); | |
1519 | } | |
1520 | return -1; | |
1521 | } | |
1522 | ConfigTop = config_read(fdn, 0, new_config_file(configfile, NULL, 0)); | |
1523 | for(p = ConfigTop; p; p = p->next) | |
1524 | #else | |
1525 | (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */ | |
1526 | while ((i = dgets(fd, line, sizeof(line) - 1)) > 0) | |
1527 | #endif | |
1528 | { | |
1529 | #if defined(CONFIG_DIRECTIVE_INCLUDE) | |
1530 | line = p->line; | |
1531 | #else | |
1532 | line[i] = '\0'; | |
1533 | if ((tmp = (char *)index(line, '\n'))) | |
1534 | *tmp = 0; | |
1535 | else while(dgets(fd, c, sizeof(c) - 1) > 0) | |
1536 | if ((tmp = (char *)index(c, '\n'))) | |
1537 | { | |
1538 | *tmp = 0; | |
1539 | break; | |
1540 | } | |
1541 | #endif | |
1542 | /* | |
1543 | * Do quoting of characters and # detection. | |
1544 | */ | |
1545 | for (tmp = line; *tmp; tmp++) | |
1546 | { | |
1547 | if (*tmp == '\\') | |
1548 | { | |
1549 | for (i = 0; quotes[i][0]; i++) | |
1550 | if (quotes[i][0] == *(tmp+1)) | |
1551 | { | |
1552 | *tmp = quotes[i][1]; | |
1553 | break; | |
1554 | } | |
1555 | if (!quotes[i][0]) | |
1556 | *tmp = *(tmp+1); | |
1557 | if (!*(tmp+1)) | |
1558 | break; | |
1559 | else | |
1560 | for (s = tmp; (*s = *(s+1)); s++) | |
1561 | ; | |
1562 | } | |
1563 | else if (*tmp == '#') | |
1564 | { | |
1565 | *tmp = '\0'; | |
1566 | break; /* Ignore the rest of the line */ | |
1567 | } | |
1568 | } | |
1569 | if (!*line || line[0] == '#' || line[0] == '\n' || | |
1570 | line[0] == ' ' || line[0] == '\t') | |
1571 | continue; | |
1572 | /* Could we test if it's conf line at all? -Vesa */ | |
1573 | if (line[1] != IRCDCONF_DELIMITER) | |
1574 | { | |
1575 | Debug((DEBUG_ERROR, "Bad config line: %s", line)); | |
1576 | continue; | |
1577 | } | |
1578 | if (aconf) | |
1579 | free_conf(aconf); | |
1580 | aconf = make_conf(); | |
1581 | ||
1582 | if (tmp2) | |
1583 | MyFree(tmp2); | |
1584 | tmp3 = tmp4 = NULL; | |
1585 | #ifdef ENABLE_CIDR_LIMITS | |
1586 | tmp5 = NULL; | |
1587 | #endif | |
1588 | tmp = getfield(line); | |
1589 | if (!tmp) | |
1590 | continue; | |
1591 | switch (*tmp) | |
1592 | { | |
1593 | case 'A': /* Name, e-mail address of administrator */ | |
1594 | case 'a': /* of this server. */ | |
1595 | aconf->status = CONF_ADMIN; | |
1596 | break; | |
1597 | case 'B': /* Name of alternate servers */ | |
1598 | case 'b': | |
1599 | aconf->status = CONF_BOUNCE; | |
1600 | break; | |
1601 | case 'C': /* Server where I should try to connect */ | |
1602 | /* in case of lp failures */ | |
1603 | ccount++; | |
1604 | aconf->status = CONF_CONNECT_SERVER; | |
1605 | break; | |
1606 | case 'c': | |
1607 | ccount++; | |
1608 | aconf->status = CONF_ZCONNECT_SERVER; | |
1609 | break; | |
1610 | case 'D': /* auto connect restrictions */ | |
1611 | case 'd': | |
1612 | aconf->status = CONF_DENY; | |
1613 | break; | |
1614 | case 'H': /* Hub server line */ | |
1615 | case 'h': | |
1616 | aconf->status = CONF_HUB; | |
1617 | break; | |
1618 | case 'i' : /* Restricted client */ | |
1619 | aconf->flags |= CFLAG_RESTRICTED; | |
1620 | case 'I': | |
1621 | aconf->status = CONF_CLIENT; | |
1622 | break; | |
1623 | case 'K': /* Kill user line on irc.conf */ | |
1624 | aconf->status = CONF_KILL; | |
1625 | break; | |
1626 | case 'k': | |
1627 | aconf->status = CONF_OTHERKILL; | |
1628 | break; | |
1629 | /* Operator. Line should contain at least */ | |
1630 | /* password and host where connection is */ | |
1631 | case 'L': /* guaranteed leaf server */ | |
1632 | case 'l': | |
1633 | aconf->status = CONF_LEAF; | |
1634 | break; | |
1635 | /* Me. Host field is name used for this host */ | |
1636 | /* and port number is the number of the port */ | |
1637 | case 'M': | |
1638 | case 'm': | |
1639 | aconf->status = CONF_ME; | |
1640 | break; | |
1641 | case 'N': /* Server where I should NOT try to */ | |
1642 | case 'n': /* connect in case of lp failures */ | |
1643 | /* but which tries to connect ME */ | |
1644 | ++ncount; | |
1645 | aconf->status = CONF_NOCONNECT_SERVER; | |
1646 | break; | |
1647 | case 'o': | |
1648 | aconf->flags |= ACL_LOCOP; | |
1649 | case 'O': | |
1650 | aconf->status = CONF_OPERATOR; | |
1651 | break; | |
1652 | case 'P': /* listen port line */ | |
1653 | case 'p': | |
1654 | aconf->status = CONF_LISTEN_PORT; | |
1655 | break; | |
1656 | case 'Q': /* a server that you don't want in your */ | |
1657 | case 'q': /* network. USE WITH CAUTION! */ | |
1658 | aconf->status = CONF_QUARANTINED_SERVER; | |
1659 | break; | |
1660 | case 'S': /* Service. Same semantics as */ | |
1661 | case 's': /* CONF_OPERATOR */ | |
1662 | aconf->status = CONF_SERVICE; | |
1663 | break; | |
1664 | case 'V': /* Server link version requirements */ | |
1665 | aconf->status = CONF_VER; | |
1666 | break; | |
1667 | case 'Y': | |
1668 | case 'y': | |
1669 | aconf->status = CONF_CLASS; | |
1670 | break; | |
1671 | #ifdef XLINE | |
1672 | case 'X': | |
1673 | aconf->status = CONF_XLINE; | |
1674 | break; | |
1675 | #endif | |
1676 | default: | |
1677 | Debug((DEBUG_ERROR, "Error in config file: %s", line)); | |
1678 | break; | |
1679 | } | |
1680 | if (IsIllegal(aconf)) | |
1681 | continue; | |
1682 | ||
1683 | do | |
1684 | { | |
1685 | if ((tmp = getfield(NULL)) == NULL) | |
1686 | break; | |
1687 | #ifdef INET6 | |
1688 | if (aconf->status & | |
1689 | (CONF_CONNECT_SERVER|CONF_ZCONNECT_SERVER | |
1690 | |CONF_CLIENT|CONF_KILL | |
1691 | |CONF_OTHERKILL|CONF_NOCONNECT_SERVER | |
1692 | |CONF_OPERATOR|CONF_LISTEN_PORT | |
1693 | |CONF_SERVICE)) | |
1694 | aconf->host = ipv6_convert(tmp); | |
1695 | else | |
1696 | DupString(aconf->host, tmp); | |
1697 | #else | |
1698 | DupString(aconf->host, tmp); | |
1699 | #endif | |
1700 | if ((tmp = getfield(NULL)) == NULL) | |
1701 | break; | |
1702 | DupString(aconf->passwd, tmp); | |
1703 | if ((tmp = getfield(NULL)) == NULL) | |
1704 | break; | |
1705 | DupString(aconf->name, tmp); | |
1706 | if ((tmp = getfield(NULL)) == NULL) | |
1707 | break; | |
1708 | #ifdef XLINE | |
1709 | if (aconf->status == CONF_XLINE) | |
1710 | { | |
1711 | DupString(aconf->name2, tmp); | |
1712 | if ((tmp = getfield(NULL)) == NULL) | |
1713 | break; | |
1714 | DupString(aconf->name3, tmp); | |
1715 | if ((tmp = getfield(NULL)) == NULL) | |
1716 | break; | |
1717 | DupString(aconf->source_ip, tmp); | |
1718 | break; | |
1719 | } | |
1720 | #endif | |
1721 | aconf->port = 0; | |
1722 | if (sscanf(tmp, "0x%x", &aconf->port) != 1 || | |
1723 | aconf->port == 0) | |
1724 | aconf->port = atoi(tmp); | |
1725 | if (aconf->status == CONF_CONNECT_SERVER) | |
1726 | DupString(tmp2, tmp); | |
1727 | if (aconf->status == CONF_ZCONNECT_SERVER) | |
1728 | DupString(tmp2, tmp); | |
1729 | if ((tmp = getfield(NULL)) == NULL) | |
1730 | break; | |
1731 | Class(aconf) = find_class(atoi(tmp)); | |
1732 | /* used in Y: local limits and I: and P: flags */ | |
1733 | if ((tmp3 = getfield(NULL)) == NULL) | |
1734 | break; | |
1735 | /* used in Y: global limits */ | |
1736 | if((tmp4 = getfield(NULL)) == NULL) | |
1737 | break; | |
1738 | #ifdef ENABLE_CIDR_LIMITS | |
1739 | tmp5 = getfield(NULL); | |
1740 | #endif | |
1741 | } while (0); /* to use break without compiler warnings */ | |
1742 | istat.is_confmem += aconf->host ? strlen(aconf->host)+1 : 0; | |
1743 | istat.is_confmem += aconf->passwd ? strlen(aconf->passwd)+1 :0; | |
1744 | istat.is_confmem += aconf->name ? strlen(aconf->name)+1 : 0; | |
1745 | istat.is_confmem += aconf->name2 ? strlen(aconf->name2)+1 : 0; | |
1746 | #ifdef XLINE | |
1747 | istat.is_confmem += aconf->name3 ? strlen(aconf->name3)+1 : 0; | |
1748 | #endif | |
1749 | istat.is_confmem += aconf->source_ip ? strlen(aconf->source_ip)+1 : 0; | |
1750 | ||
1751 | /* | |
1752 | ** Bounce line fields are mandatory | |
1753 | */ | |
1754 | if (aconf->status == CONF_BOUNCE && aconf->port == 0) | |
1755 | continue; | |
1756 | /* | |
1757 | ** If conf line is a class definition, create a class entry | |
1758 | ** for it and make the conf_line illegal and delete it. | |
1759 | */ | |
1760 | if (aconf->status & CONF_CLASS) | |
1761 | { | |
1762 | if (atoi(aconf->host) >= 0) | |
1763 | add_class(atoi(aconf->host), | |
1764 | atoi(aconf->passwd), | |
1765 | atoi(aconf->name), aconf->port, | |
1766 | tmp ? atoi(tmp) : 0, | |
1767 | (tmp && index(tmp, '.')) ? | |
1768 | atoi(index(tmp, '.') + 1) : 0, | |
1769 | tmp3 ? atoi(tmp3) : 1, | |
1770 | (tmp3 && index(tmp3, '.')) ? | |
1771 | atoi(index(tmp3, '.') + 1) : 1, | |
1772 | tmp4 ? atoi(tmp4) : 1, | |
1773 | (tmp4 && index(tmp4, '.')) ? | |
1774 | atoi(index(tmp4, '.') + 1) : 1 | |
1775 | #ifdef ENABLE_CIDR_LIMITS | |
1776 | , tmp5 | |
1777 | #endif | |
1778 | ); | |
1779 | continue; | |
1780 | } | |
1781 | /* | |
1782 | ** associate each conf line with a class by using a pointer | |
1783 | ** to the correct class record. -avalon | |
1784 | */ | |
1785 | if (aconf->status & (CONF_CLIENT_MASK|CONF_LISTEN_PORT)) | |
1786 | { | |
1787 | if (Class(aconf) == 0) | |
1788 | Class(aconf) = find_class(0); | |
1789 | if (MaxLinks(Class(aconf)) < 0) | |
1790 | Class(aconf) = find_class(0); | |
1791 | } | |
1792 | if (aconf->status & (CONF_LISTEN_PORT|CONF_CLIENT)) | |
1793 | { | |
1794 | aConfItem *bconf; | |
1795 | ||
1796 | /* any flags in this line? */ | |
1797 | if (tmp3) | |
1798 | { | |
1799 | aconf->flags |= | |
1800 | ((aconf->status == CONF_CLIENT) ? | |
1801 | iline_flags_parse(tmp3) : | |
1802 | pline_flags_parse(tmp3)); | |
1803 | } | |
1804 | ||
1805 | /* trying to find exact conf line in already existing | |
1806 | * conf, so we don't delete old one, just update it */ | |
1807 | if ( | |
1808 | #ifdef FASTER_ILINE_REHASH | |
1809 | (aconf->status & CONF_LISTEN_PORT) && | |
1810 | #endif | |
1811 | (bconf = find_conf_entry(aconf, aconf->status))) | |
1812 | { | |
1813 | /* we remove bconf (already existing) from conf | |
1814 | * so that we can add it back uniformly at the | |
1815 | * end of while(dgets) loop. --B. */ | |
1816 | delist_conf(bconf); | |
1817 | bconf->status &= ~CONF_ILLEGAL; | |
1818 | /* aconf is a new item, it can contain +r flag | |
1819 | * (from lowercase i:lines). In any case we | |
1820 | * don't want old flags to remain. --B. */ | |
1821 | bconf->flags = aconf->flags; | |
1822 | /* in case class was changed */ | |
1823 | if (aconf->status == CONF_CLIENT && | |
1824 | aconf->class != bconf->class) | |
1825 | { | |
1826 | bconf->class->links -= bconf->clients; | |
1827 | bconf->class = aconf->class; | |
1828 | bconf->class->links += bconf->clients; | |
1829 | } | |
1830 | /* free new one, assign old one to aconf */ | |
1831 | free_conf(aconf); | |
1832 | aconf = bconf; | |
1833 | } | |
1834 | else /* no such conf line was found */ | |
1835 | { | |
1836 | if (aconf->host && | |
1837 | aconf->status == CONF_LISTEN_PORT) | |
1838 | { | |
1839 | (void)add_listener(aconf); | |
1840 | } | |
1841 | } | |
1842 | } | |
1843 | if (aconf->status & CONF_SERVICE) | |
1844 | aconf->port &= SERVICE_MASK_ALL; | |
1845 | if (aconf->status & (CONF_SERVER_MASK|CONF_SERVICE)) | |
1846 | { | |
1847 | char *hostptr = NULL; | |
1848 | ||
1849 | /* since it's u@h syntax, let's ignore user part | |
1850 | in checks below --B. */ | |
1851 | hostptr = index(aconf->host, '@'); | |
1852 | if (hostptr != NULL) | |
1853 | hostptr++; /* move ptr after '@' */ | |
1854 | else | |
1855 | hostptr = aconf->host; | |
1856 | ||
1857 | if (ncount > MAXCONFLINKS || ccount > MAXCONFLINKS | |
1858 | || !hostptr || index(hostptr, '*') | |
1859 | || index(hostptr,'?') || !aconf->name) | |
1860 | continue; /* next config line */ | |
1861 | } | |
1862 | ||
1863 | if (aconf->status & | |
1864 | (CONF_SERVER_MASK|CONF_OPERATOR|CONF_SERVICE)) | |
1865 | if (!index(aconf->host, '@') && *aconf->host != '/') | |
1866 | { | |
1867 | char *newhost; | |
1868 | int len = 3; /* *@\0 = 3 */ | |
1869 | ||
1870 | len += strlen(aconf->host); | |
1871 | newhost = (char *)MyMalloc(len); | |
1872 | sprintf(newhost, "*@%s", aconf->host); | |
1873 | MyFree(aconf->host); | |
1874 | aconf->host = newhost; | |
1875 | istat.is_confmem += 2; | |
1876 | } | |
1877 | if (tmp3 && (aconf->status & CONF_OPERATOR)) | |
1878 | { | |
1879 | aconf->flags |= oline_flags_parse(tmp3); | |
1880 | /* remove this when removing o: lines --B. */ | |
1881 | if (aconf->flags & ACL_LOCOP) | |
1882 | aconf->flags &= ~ACL_ALL_REMOTE; | |
1883 | } | |
1884 | if (aconf->status & CONF_SERVER_MASK) | |
1885 | { | |
1886 | if (BadPtr(aconf->passwd)) | |
1887 | continue; | |
1888 | else if (!(opt & BOOT_QUICK)) | |
1889 | (void)lookup_confhost(aconf); | |
1890 | } | |
1891 | if (aconf->status & (CONF_CONNECT_SERVER | CONF_ZCONNECT_SERVER)) | |
1892 | { | |
1893 | aconf->ping = (aCPing *)MyMalloc(sizeof(aCPing)); | |
1894 | bzero((char *)aconf->ping, sizeof(*aconf->ping)); | |
1895 | istat.is_confmem += sizeof(*aconf->ping); | |
1896 | if (tmp2 && index(tmp2, '.')) | |
1897 | aconf->ping->port = atoi(index(tmp2, '.') + 1); | |
1898 | else | |
1899 | aconf->ping->port = aconf->port; | |
1900 | if (tmp2) | |
1901 | { | |
1902 | MyFree(tmp2); | |
1903 | tmp2 = NULL; | |
1904 | } | |
1905 | if (tmp3) | |
1906 | { | |
1907 | DupString(aconf->source_ip, tmp3); | |
1908 | } | |
1909 | ||
1910 | } | |
1911 | /* | |
1912 | ** Name cannot be changed after the startup. | |
1913 | ** (or could be allowed, but only if all links are closed | |
1914 | ** first). | |
1915 | ** Configuration info does not override the name and port | |
1916 | ** if previously defined. Note, that "info"-field can be | |
1917 | ** changed by "/rehash". | |
1918 | */ | |
1919 | if (aconf->status == CONF_ME) | |
1920 | { | |
1921 | if (me.info != DefInfo) | |
1922 | MyFree(me.info); | |
1923 | me.info = MyMalloc(REALLEN+1); | |
1924 | strncpyzt(me.info, aconf->name, REALLEN+1); | |
1925 | if (me.serv->namebuf[0] == '\0' && aconf->host[0]) | |
1926 | strncpyzt(me.serv->namebuf, aconf->host, | |
1927 | sizeof(me.serv->namebuf)); | |
1928 | if (me.serv->sid[0] == '\0' && tmp && *tmp) | |
1929 | { | |
1930 | for(i = 0; i < sizeof(me.serv->sid); i++) | |
1931 | me.serv->sid[i] = toupper(tmp[i]); | |
1932 | } | |
1933 | ||
1934 | if (aconf->port) | |
1935 | setup_ping(aconf); | |
1936 | } | |
1937 | ||
1938 | if (aconf->status == CONF_ADMIN) | |
1939 | { | |
1940 | if (!networkname && tmp && *tmp) | |
1941 | { | |
1942 | if (strlen(tmp) < HOSTLEN) | |
1943 | { | |
1944 | DupString(networkname,tmp); | |
1945 | } | |
1946 | } | |
1947 | } | |
1948 | ||
1949 | (void)collapse(aconf->host); | |
1950 | (void)collapse(aconf->name); | |
1951 | Debug((DEBUG_NOTICE, | |
1952 | "Read Init: (%d) (%s) (%s) (%s) (%d) (%d)", | |
1953 | aconf->status, aconf->host, aconf->passwd, | |
1954 | aconf->name, aconf->port, | |
1955 | aconf->class ? ConfClass(aconf) : 0)); | |
1956 | ||
1957 | if (aconf->status & (CONF_KILL|CONF_OTHERKILL)) | |
1958 | { | |
1959 | aconf->next = kconf; | |
1960 | kconf = aconf; | |
1961 | } | |
1962 | else | |
1963 | { | |
1964 | aconf->next = conf; | |
1965 | conf = aconf; | |
1966 | } | |
1967 | aconf = NULL; | |
1968 | } | |
1969 | #if defined(CONFIG_DIRECTIVE_INCLUDE) | |
1970 | config_free(ConfigTop); | |
1971 | #endif | |
1972 | if (aconf) | |
1973 | free_conf(aconf); | |
1974 | (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */ | |
1975 | #if defined(CONFIG_DIRECTIVE_INCLUDE) | |
1976 | (void)fclose(fdn); | |
1977 | #else | |
1978 | (void)close(fd); | |
1979 | #endif | |
1980 | #if defined(M4_PREPROC) && !defined(USE_IAUTH) | |
1981 | (void)wait(0); | |
1982 | #endif | |
1983 | check_class(); | |
1984 | nextping = nextconnect = timeofday; | |
1985 | return 0; | |
1986 | } | |
1987 | ||
1988 | /* | |
1989 | * lookup_confhost | |
1990 | * Do (start) DNS lookups of all hostnames in the conf line and convert | |
1991 | * an IP addresses in a.b.c.d number for to IP#s. | |
1992 | */ | |
1993 | static int lookup_confhost(aConfItem *aconf) | |
1994 | { | |
1995 | Reg char *s; | |
1996 | Reg struct hostent *hp; | |
1997 | Link ln; | |
1998 | ||
1999 | if (BadPtr(aconf->host) || BadPtr(aconf->name)) | |
2000 | goto badlookup; | |
2001 | if ((s = index(aconf->host, '@'))) | |
2002 | s++; | |
2003 | else | |
2004 | s = aconf->host; | |
2005 | /* | |
2006 | ** Do name lookup now on hostnames given and store the | |
2007 | ** ip numbers in conf structure. | |
2008 | */ | |
2009 | if (!isalpha(*s) && !isdigit(*s)) | |
2010 | goto badlookup; | |
2011 | ||
2012 | /* | |
2013 | ** Prepare structure in case we have to wait for a | |
2014 | ** reply which we get later and store away. | |
2015 | */ | |
2016 | ln.value.aconf = aconf; | |
2017 | ln.flags = ASYNC_CONF; | |
2018 | ||
2019 | #ifdef INET6 | |
2020 | if(inetpton(AF_INET6, s, aconf->ipnum.s6_addr)) | |
2021 | ; | |
2022 | #else | |
2023 | if (isdigit(*s)) | |
2024 | aconf->ipnum.s_addr = inetaddr(s); | |
2025 | #endif | |
2026 | else if ((hp = gethost_byname(s, &ln))) | |
2027 | bcopy(hp->h_addr, (char *)&(aconf->ipnum), | |
2028 | sizeof(struct IN_ADDR)); | |
2029 | #ifdef INET6 | |
2030 | else | |
2031 | { | |
2032 | bcopy(minus_one, aconf->ipnum.s6_addr, IN6ADDRSZ); | |
2033 | goto badlookup; | |
2034 | } | |
2035 | ||
2036 | #else | |
2037 | if (aconf->ipnum.s_addr == -1) | |
2038 | goto badlookup; | |
2039 | #endif | |
2040 | ||
2041 | return 0; | |
2042 | ||
2043 | badlookup: | |
2044 | #ifdef INET6 | |
2045 | if (AND16(aconf->ipnum.s6_addr) == 255) | |
2046 | #else | |
2047 | if (aconf->ipnum.s_addr == -1) | |
2048 | #endif | |
2049 | bzero((char *)&aconf->ipnum, sizeof(struct IN_ADDR)); | |
2050 | Debug((DEBUG_ERROR,"Host/server name error: (%s) (%s)", | |
2051 | aconf->host, aconf->name)); | |
2052 | return -1; | |
2053 | } | |
2054 | ||
2055 | int find_kill(aClient *cptr, int timedklines, char **comment) | |
2056 | { | |
2057 | #ifdef TIMEDKLINES | |
2058 | static char reply[256]; | |
2059 | int now = 0; | |
2060 | #endif | |
2061 | char *host, *ip, *name, *ident, *check; | |
2062 | aConfItem *tmp; | |
2063 | #ifdef TKLINE | |
2064 | int tklines = 1; | |
2065 | #endif | |
2066 | ||
2067 | if (!cptr->user) | |
2068 | return 0; | |
2069 | ||
2070 | if (IsKlineExempt(cptr)) | |
2071 | { | |
2072 | return 0; | |
2073 | } | |
2074 | ||
2075 | host = cptr->sockhost; | |
2076 | #ifdef INET6 | |
2077 | ip = (char *) inetntop(AF_INET6, (char *)&cptr->ip, ipv6string, | |
2078 | sizeof(ipv6string)); | |
2079 | #else | |
2080 | ip = (char *) inetntoa((char *)&cptr->ip); | |
2081 | #endif | |
2082 | if (!strcmp(host, ip)) | |
2083 | ip = NULL; /* we don't have a name for the ip# */ | |
2084 | name = cptr->user->username; | |
2085 | if (IsRestricted(cptr) && name[0] == '+') | |
2086 | { | |
2087 | /* | |
2088 | ** since we added '+' at the begining of valid | |
2089 | ** ident response, remove it here for kline | |
2090 | ** comparison --Beeth | |
2091 | */ | |
2092 | name++; | |
2093 | } | |
2094 | ident = cptr->auth; | |
2095 | ||
2096 | if (strlen(host) > (size_t) HOSTLEN || | |
2097 | (name ? strlen(name) : 0) > (size_t) HOSTLEN) | |
2098 | return (0); | |
2099 | ||
2100 | #ifdef TIMEDKLINES | |
2101 | *reply = '\0'; | |
2102 | #endif | |
2103 | ||
2104 | findkline: | |
2105 | tmp = | |
2106 | #ifdef TKLINE | |
2107 | tklines ? tkconf : | |
2108 | #endif | |
2109 | kconf; | |
2110 | for (; tmp; tmp = tmp->next) | |
2111 | { | |
2112 | #ifdef TIMEDKLINES | |
2113 | if (timedklines && (BadPtr(tmp->passwd) || !isdigit(*tmp->passwd))) | |
2114 | continue; | |
2115 | #endif | |
2116 | if (!(tmp->status & ( | |
2117 | #ifdef TKLINE | |
2118 | tklines ? (CONF_TKILL | CONF_TOTHERKILL) : | |
2119 | #endif | |
2120 | (CONF_KILL | CONF_OTHERKILL)))) | |
2121 | continue; /* should never happen with kconf */ | |
2122 | if (!tmp->host || !tmp->name) | |
2123 | continue; | |
2124 | #ifdef TKLINE | |
2125 | /* this TK already expired */ | |
2126 | if (tklines && tmp->hold < timeofday) | |
2127 | continue; | |
2128 | #endif | |
2129 | if (tmp->status == ( | |
2130 | #ifdef TKLINE | |
2131 | tklines ? CONF_TKILL : | |
2132 | #endif | |
2133 | CONF_KILL)) | |
2134 | check = name; | |
2135 | else | |
2136 | check = ident; | |
2137 | /* host & IP matching.. */ | |
2138 | if (!ip) /* unresolved */ | |
2139 | { | |
2140 | if (strchr(tmp->host, '/')) | |
2141 | { | |
2142 | if (match_ipmask((*tmp->host == '=') ? | |
2143 | tmp->host+1: tmp->host, cptr, 1)) | |
2144 | continue; | |
2145 | } | |
2146 | else | |
2147 | if (match((*tmp->host == '=') ? tmp->host+1 : | |
2148 | tmp->host, host)) | |
2149 | continue; | |
2150 | } | |
2151 | else if (*tmp->host == '=') /* numeric only */ | |
2152 | continue; | |
2153 | else /* resolved */ | |
2154 | if (strchr(tmp->host, '/')) | |
2155 | { | |
2156 | if (match_ipmask(tmp->host, cptr, 1)) | |
2157 | continue; | |
2158 | } | |
2159 | else | |
2160 | if (match(tmp->host, ip) && | |
2161 | match(tmp->host, host)) | |
2162 | continue; | |
2163 | ||
2164 | /* user & port matching */ | |
2165 | if ((!check || match(tmp->name, check) == 0) && | |
2166 | (!tmp->port || (tmp->port == cptr->acpt->port))) | |
2167 | { | |
2168 | #ifdef TIMEDKLINES | |
2169 | now = 0; | |
2170 | if (!BadPtr(tmp->passwd) && isdigit(*tmp->passwd) && | |
2171 | !(now = check_time_interval(tmp->passwd, reply))) | |
2172 | continue; | |
2173 | if (now == ERR_YOUWILLBEBANNED) | |
2174 | tmp = NULL; | |
2175 | #endif | |
2176 | break; | |
2177 | } | |
2178 | } | |
2179 | #ifdef TKLINE | |
2180 | if (!tmp && tklines) | |
2181 | { | |
2182 | tklines = 0; | |
2183 | goto findkline; | |
2184 | } | |
2185 | #endif | |
2186 | if (tmp && !BadPtr(tmp->passwd)) | |
2187 | { | |
2188 | *comment = tmp->passwd; | |
2189 | } | |
2190 | else | |
2191 | { | |
2192 | *comment = NULL; | |
2193 | } | |
2194 | #ifdef TIMEDKLINES | |
2195 | if (*reply) | |
2196 | { | |
2197 | sendto_one(cptr, reply, ME, now, cptr->name); | |
2198 | } | |
2199 | else | |
2200 | #endif | |
2201 | if (tmp) | |
2202 | { | |
2203 | sendto_one(cptr, replies[ERR_YOUREBANNEDCREEP], | |
2204 | ME, cptr->name, | |
2205 | BadPtr(tmp->name) ? "*" : tmp->name, | |
2206 | BadPtr(tmp->host) ? "*" : tmp->host, | |
2207 | *comment ? ": " : "", | |
2208 | *comment ? *comment : ""); | |
2209 | } | |
2210 | ||
2211 | return (tmp ? -1 : 0); | |
2212 | } | |
2213 | ||
2214 | /* | |
2215 | * For type stat, check if both name and host masks match. | |
2216 | * Return -1 for match, 0 for no-match. | |
2217 | */ | |
2218 | int find_two_masks(char *name, char *host, int stat) | |
2219 | { | |
2220 | aConfItem *tmp; | |
2221 | ||
2222 | for (tmp = conf; tmp; tmp = tmp->next) | |
2223 | if ((tmp->status == stat) && tmp->host && tmp->name && | |
2224 | (match(tmp->host, host) == 0) && | |
2225 | (match(tmp->name, name) == 0)) | |
2226 | break; | |
2227 | return (tmp ? -1 : 0); | |
2228 | } | |
2229 | ||
2230 | /* | |
2231 | * For type stat, check if name matches and any char from key matches | |
2232 | * to chars in passwd field. | |
2233 | * Return -1 for match, 0 for no-match. | |
2234 | */ | |
2235 | int find_conf_flags(char *name, char *key, int stat) | |
2236 | { | |
2237 | aConfItem *tmp; | |
2238 | int l; | |
2239 | ||
2240 | if (index(key, '/') == NULL) | |
2241 | return 0; | |
2242 | l = ((char *)index(key, '/') - key) + 1; | |
2243 | for (tmp = conf; tmp; tmp = tmp->next) | |
2244 | if ((tmp->status == stat) && tmp->passwd && tmp->name && | |
2245 | (strncasecmp(key, tmp->passwd, l) == 0) && | |
2246 | (match(tmp->name, name) == 0) && | |
2247 | (strpbrk(key + l, tmp->passwd + l))) | |
2248 | break; | |
2249 | return (tmp ? -1 : 0); | |
2250 | } | |
2251 | ||
2252 | #ifdef TIMEDKLINES | |
2253 | /* | |
2254 | ** check against a set of time intervals | |
2255 | */ | |
2256 | ||
2257 | static int check_time_interval(char *interval, char *reply) | |
2258 | { | |
2259 | struct tm *tptr; | |
2260 | char *p; | |
2261 | int perm_min_hours, perm_min_minutes, | |
2262 | perm_max_hours, perm_max_minutes; | |
2263 | int now, perm_min, perm_max; | |
2264 | ||
2265 | tptr = localtime(&timeofday); | |
2266 | now = tptr->tm_hour * 60 + tptr->tm_min; | |
2267 | ||
2268 | while (interval) | |
2269 | { | |
2270 | p = (char *)index(interval, ','); | |
2271 | if (p) | |
2272 | *p = '\0'; | |
2273 | if (sscanf(interval, "%2d%2d-%2d%2d", | |
2274 | &perm_min_hours, &perm_min_minutes, | |
2275 | &perm_max_hours, &perm_max_minutes) != 4) | |
2276 | { | |
2277 | if (p) | |
2278 | *p = ','; | |
2279 | return(0); | |
2280 | } | |
2281 | if (p) | |
2282 | *(p++) = ','; | |
2283 | perm_min = 60 * perm_min_hours + perm_min_minutes; | |
2284 | perm_max = 60 * perm_max_hours + perm_max_minutes; | |
2285 | /* | |
2286 | ** The following check allows intervals over midnight ... | |
2287 | */ | |
2288 | if ((perm_min < perm_max) | |
2289 | ? (perm_min <= now && now <= perm_max) | |
2290 | : (perm_min <= now || now <= perm_max)) | |
2291 | { | |
2292 | (void)sprintf(reply, | |
2293 | ":%%s %%d %%s :%s %d:%02d to %d:%02d.", | |
2294 | "You are not allowed to connect from", | |
2295 | perm_min_hours, perm_min_minutes, | |
2296 | perm_max_hours, perm_max_minutes); | |
2297 | return(ERR_YOUREBANNEDCREEP); | |
2298 | } | |
2299 | if ((perm_min < perm_max) | |
2300 | ? (perm_min <= now + 5 && now + 5 <= perm_max) | |
2301 | : (perm_min <= now + 5 || now + 5 <= perm_max)) | |
2302 | { | |
2303 | (void)sprintf(reply, ":%%s %%d %%s :%d minute%s%s", | |
2304 | perm_min-now,(perm_min-now)>1?"s ":" ", | |
2305 | "and you will be denied for further access"); | |
2306 | return(ERR_YOUWILLBEBANNED); | |
2307 | } | |
2308 | interval = p; | |
2309 | } | |
2310 | return(0); | |
2311 | } | |
2312 | #endif /* TIMEDKLINES */ | |
2313 | ||
2314 | /* | |
2315 | ** find_bounce | |
2316 | ** send a bounce numeric to a client. | |
2317 | ** fd is optional, and only makes sense if positive and when cptr is NULL | |
2318 | ** fd == -1 : not fd, class is a class number. | |
2319 | ** fd == -2 : not fd, class isn't a class number. | |
2320 | */ | |
2321 | void find_bounce(aClient *cptr, int class, int fd) | |
2322 | { | |
2323 | Reg aConfItem *aconf; | |
2324 | ||
2325 | if (fd < 0 && cptr == NULL) | |
2326 | { | |
2327 | /* nowhere to send error to */ | |
2328 | return; | |
2329 | } | |
2330 | ||
2331 | for (aconf = conf; aconf; aconf = aconf->next) | |
2332 | { | |
2333 | if (aconf->status != CONF_BOUNCE) | |
2334 | { | |
2335 | continue; | |
2336 | } | |
2337 | ||
2338 | if (fd >= 0) | |
2339 | { | |
2340 | /* | |
2341 | ** early rejection, | |
2342 | ** connection class and hostname are unknown | |
2343 | */ | |
2344 | if (*aconf->host == '\0') | |
2345 | { | |
2346 | char rpl[BUFSIZE]; | |
2347 | ||
2348 | sprintf(rpl, replies[RPL_BOUNCE], ME, "unknown", | |
2349 | aconf->name, aconf->port); | |
2350 | strcat(rpl, "\r\n"); | |
2351 | #ifdef INET6 | |
2352 | sendto(fd, rpl, strlen(rpl), 0, 0, 0); | |
2353 | #else | |
2354 | send(fd, rpl, strlen(rpl), 0); | |
2355 | #endif | |
2356 | return; | |
2357 | } | |
2358 | else | |
2359 | { | |
2360 | continue; | |
2361 | } | |
2362 | } | |
2363 | ||
2364 | /* fd < 0 */ | |
2365 | /* | |
2366 | ** "too many" type rejection, class is known. | |
2367 | ** check if B line is for a class #, | |
2368 | ** and if it is for a hostname. | |
2369 | */ | |
2370 | if (fd != -2 && | |
2371 | !strchr(aconf->host, '.') && | |
2372 | (isdigit(*aconf->host) || *aconf->host == '-')) | |
2373 | { | |
2374 | if (class != atoi(aconf->host)) | |
2375 | { | |
2376 | continue; | |
2377 | } | |
2378 | } | |
2379 | else | |
2380 | { | |
2381 | if (strchr(aconf->host, '/')) | |
2382 | { | |
2383 | if (match_ipmask(aconf->host, cptr, 1)) | |
2384 | continue; | |
2385 | } | |
2386 | else if (match(aconf->host, cptr->sockhost)) | |
2387 | { | |
2388 | continue; | |
2389 | } | |
2390 | } | |
2391 | ||
2392 | sendto_one(cptr, replies[RPL_BOUNCE], ME, BadTo(cptr->name), | |
2393 | aconf->name, aconf->port); | |
2394 | return; | |
2395 | } | |
2396 | ||
2397 | } | |
2398 | ||
2399 | /* | |
2400 | ** find_denied | |
2401 | ** for a given server name, make sure no D line matches any of the | |
2402 | ** servers currently present on the net. | |
2403 | */ | |
2404 | aConfItem *find_denied(char *name, int class) | |
2405 | { | |
2406 | aConfItem *aconf; | |
2407 | ||
2408 | for (aconf = conf; aconf; aconf = aconf->next) | |
2409 | { | |
2410 | if (aconf->status != CONF_DENY) | |
2411 | continue; | |
2412 | if (!aconf->name) | |
2413 | continue; | |
2414 | if (match(aconf->name, name) && aconf->port != class) | |
2415 | continue; | |
2416 | if (isdigit(*aconf->passwd)) | |
2417 | { | |
2418 | aConfItem *aconf2; | |
2419 | int ck = atoi(aconf->passwd); | |
2420 | ||
2421 | for (aconf2 = conf; aconf2; aconf2 = aconf2->next) | |
2422 | { | |
2423 | if (aconf2->status != CONF_NOCONNECT_SERVER) | |
2424 | continue; | |
2425 | if (!aconf2->class || ConfClass(aconf2) != ck) | |
2426 | continue; | |
2427 | if (find_client(aconf2->name, NULL)) | |
2428 | return aconf2; | |
2429 | } | |
2430 | } | |
2431 | if (aconf->host) | |
2432 | { | |
2433 | aServer *asptr; | |
2434 | char *host = aconf->host; | |
2435 | int reversed = 0; | |
2436 | ||
2437 | if (*host == '!') | |
2438 | { | |
2439 | host++; | |
2440 | reversed = 1; | |
2441 | } | |
2442 | for (asptr = svrtop; asptr; asptr = asptr->nexts) | |
2443 | if (!match(host, asptr->bcptr->name)) | |
2444 | break; | |
2445 | ||
2446 | if (!reversed && asptr) | |
2447 | return aconf; | |
2448 | if (reversed && !asptr) | |
2449 | /* anything but NULL; tho using it may give | |
2450 | ** funny results in calling function */ | |
2451 | return conf; | |
2452 | } | |
2453 | } | |
2454 | return NULL; | |
2455 | } | |
2456 | ||
2457 | #ifdef TKLINE | |
2458 | /* | |
2459 | * Parses 0w1d2h3m4s timeformat, filling in output variable in seconds. | |
2460 | * Returns 0 if everything went ok. | |
2461 | */ | |
2462 | int wdhms2sec(char *input, time_t *output) | |
2463 | { | |
2464 | #ifndef TKLINE_MULTIPLIER | |
2465 | #define TKLINE_MULTIPLIER 60 | |
2466 | #endif | |
2467 | int multi; | |
2468 | int tmp = 0; | |
2469 | char *s; | |
2470 | ||
2471 | *output = 0; | |
2472 | ||
2473 | if (!input) return 0; | |
2474 | ||
2475 | s = input; | |
2476 | while (*s) | |
2477 | { | |
2478 | switch(tolower(*s)) | |
2479 | { | |
2480 | case 'w': | |
2481 | multi = 604800; break; | |
2482 | case 'd': | |
2483 | multi = 86400; break; | |
2484 | case 'h': | |
2485 | multi = 3600; break; | |
2486 | case 'm': | |
2487 | multi = 60; break; | |
2488 | case 's': | |
2489 | multi = 1; break; | |
2490 | default: | |
2491 | if (isdigit(*s)) | |
2492 | { | |
2493 | tmp = atoi(s); | |
2494 | while (isdigit(*s)) | |
2495 | s++; | |
2496 | if (!*s) | |
2497 | { | |
2498 | *output += TKLINE_MULTIPLIER * tmp; | |
2499 | } | |
2500 | continue; | |
2501 | } | |
2502 | else | |
2503 | return -1; | |
2504 | } | |
2505 | *output += multi * tmp; | |
2506 | s++; | |
2507 | } | |
2508 | return 0; | |
2509 | } | |
2510 | #endif | |
2511 | ||
2512 | #if defined(TKLINE) || defined(KLINE) | |
2513 | /* | |
2514 | * Adds t/kline to t/kconf. | |
2515 | * If tkline already existed, its expire time is updated. | |
2516 | * | |
2517 | * Returns created tkline expire time. | |
2518 | */ | |
2519 | void do_kline(int tkline, char *who, time_t time, char *user, char *host, char *reason, int status) | |
2520 | { | |
2521 | char buff[BUFSIZE]; | |
2522 | aClient *acptr; | |
2523 | aConfItem *aconf; | |
2524 | int i, count = 0; | |
2525 | ||
2526 | buff[0] = '\0'; | |
2527 | ||
2528 | /* Check if such u@h already exists in tkconf. */ | |
2529 | for (aconf = tkline?tkconf:kconf; aconf; aconf = aconf->next) | |
2530 | { | |
2531 | if (0==strcasecmp(aconf->host, host) && | |
2532 | 0==strcasecmp(aconf->name, user)) | |
2533 | { | |
2534 | aconf->hold = timeofday + time; | |
2535 | break; | |
2536 | } | |
2537 | } | |
2538 | if (aconf == NULL) | |
2539 | { | |
2540 | aconf = make_conf(); | |
2541 | aconf->next = NULL; | |
2542 | aconf->status = status; | |
2543 | aconf->hold = timeofday + time; | |
2544 | aconf->port = 0; | |
2545 | Class(aconf) = find_class(0); | |
2546 | DupString(aconf->name, BadTo(user)); | |
2547 | DupString(aconf->host, BadTo(host)); | |
2548 | DupString(aconf->passwd, reason); | |
2549 | istat.is_confmem += strlen(aconf->name) + 1; | |
2550 | istat.is_confmem += strlen(aconf->host) + 1; | |
2551 | istat.is_confmem += strlen(aconf->passwd) + 1; | |
2552 | ||
2553 | /* put on top of t/kconf */ | |
2554 | if (tkline) | |
2555 | { | |
2556 | if (tkconf) | |
2557 | { | |
2558 | aconf->next = tkconf; | |
2559 | } | |
2560 | tkconf = aconf; | |
2561 | sendto_flag(SCH_TKILL, "TKLINE %s@%s (%u) by %s :%s", | |
2562 | aconf->name, aconf->host, time, who, reason); | |
2563 | } | |
2564 | else | |
2565 | { | |
2566 | if (kconf) | |
2567 | { | |
2568 | aconf->next = kconf; | |
2569 | } | |
2570 | kconf = aconf; | |
2571 | sendto_flag(SCH_TKILL, "KLINE %s@%s by %s :%s", | |
2572 | aconf->name, aconf->host, who, reason); | |
2573 | } | |
2574 | } | |
2575 | ||
2576 | /* get rid of klined clients */ | |
2577 | for (i = highest_fd; i >= 0; i--) | |
2578 | { | |
2579 | if (!(acptr = local[i]) || !IsPerson(acptr) | |
2580 | || IsKlineExempt(acptr)) | |
2581 | { | |
2582 | continue; | |
2583 | } | |
2584 | if (!strcmp(acptr->sockhost, acptr->user->sip)) | |
2585 | { | |
2586 | /* unresolved */ | |
2587 | if (strchr(aconf->host, '/')) | |
2588 | { | |
2589 | if (match_ipmask(*aconf->host == '=' ? | |
2590 | aconf->host + 1 : aconf->host, | |
2591 | acptr, 1)) | |
2592 | { | |
2593 | continue; | |
2594 | } | |
2595 | } | |
2596 | else | |
2597 | { | |
2598 | if (match(*aconf->host == '=' ? | |
2599 | aconf->host + 1 : aconf->host, | |
2600 | acptr->sockhost)) | |
2601 | { | |
2602 | continue; | |
2603 | } | |
2604 | } | |
2605 | } | |
2606 | else | |
2607 | { | |
2608 | /* resolved */ | |
2609 | if (*aconf->host == '=') | |
2610 | { | |
2611 | /* IP only */ | |
2612 | continue; | |
2613 | } | |
2614 | if (strchr(aconf->host, '/')) | |
2615 | { | |
2616 | if (match_ipmask(aconf->host, acptr, 1)) | |
2617 | { | |
2618 | continue; | |
2619 | } | |
2620 | } | |
2621 | else | |
2622 | { | |
2623 | if (match(aconf->host, acptr->user->sip) | |
2624 | && match(aconf->host, | |
2625 | acptr->user->host)) | |
2626 | { | |
2627 | continue; | |
2628 | } | |
2629 | } | |
2630 | } | |
2631 | if (match(aconf->name, aconf->status == CONF_TOTHERKILL ? | |
2632 | acptr->auth : (IsRestricted(acptr) && | |
2633 | acptr->user->username[0] == '+' ? | |
2634 | acptr->user->username+1 : | |
2635 | acptr->user->username)) == 0) | |
2636 | { | |
2637 | count++; | |
2638 | sendto_one(acptr, replies[ERR_YOUREBANNEDCREEP], | |
2639 | ME, acptr->name, aconf->name, aconf->host, | |
2640 | ": ", aconf->passwd); | |
2641 | sendto_flag(SCH_TKILL, | |
2642 | "%sKill line active for %s", tkline?"T":"", | |
2643 | get_client_name(acptr, FALSE)); | |
2644 | if (buff[0] == '\0') | |
2645 | { | |
2646 | sprintf(buff, "Kill line active: %.80s", | |
2647 | aconf->passwd); | |
2648 | } | |
2649 | acptr->exitc = tkline ? EXITC_TKLINE : EXITC_KLINE; | |
2650 | (void) exit_client(acptr, acptr, &me, buff); | |
2651 | } | |
2652 | } | |
2653 | if (count > 4) | |
2654 | { | |
2655 | sendto_flag(SCH_TKILL, "%sKline reaped %d souls", | |
2656 | tkline?"T":"", count); | |
2657 | } | |
2658 | ||
2659 | #ifdef TKLINE | |
2660 | /* do next tkexpire, but not more often than once a minute */ | |
2661 | if (!nexttkexpire || nexttkexpire > aconf->hold) | |
2662 | { | |
2663 | nexttkexpire = MAX(timeofday + 60, aconf->hold); | |
2664 | } | |
2665 | #endif | |
2666 | return; | |
2667 | } | |
2668 | ||
2669 | int prep_kline(int tkline, aClient *cptr, aClient *sptr, int parc, char **parv) | |
2670 | { | |
2671 | int status = tkline ? CONF_TKILL : CONF_KILL; | |
2672 | time_t time; | |
2673 | char *user, *host, *reason; | |
2674 | int i = 0; | |
2675 | ||
2676 | /* sanity checks */ | |
2677 | if (tkline) | |
2678 | { | |
2679 | i = wdhms2sec(parv[1], &time); | |
2680 | #ifdef TKLINE_MAXTIME | |
2681 | if (time > TKLINE_MAXTIME) | |
2682 | time = TKLINE_MAXTIME; | |
2683 | if (time < 0) /* overflown, must have wanted bignum :) */ | |
2684 | time = TKLINE_MAXTIME; | |
2685 | #endif | |
2686 | user = parv[2]; | |
2687 | reason = parv[3]; | |
2688 | } | |
2689 | else | |
2690 | { | |
2691 | user = parv[1]; | |
2692 | reason = parv[2]; | |
2693 | } | |
2694 | host = strchr(user, '@'); | |
2695 | ||
2696 | if (strlen(user) > USERLEN+HOSTLEN+1) | |
2697 | { | |
2698 | /* induce error */ | |
2699 | i = 1; | |
2700 | } | |
2701 | if (!strcmp("@*", user) || !strcmp("*@", user) || !strcmp("@", user)) | |
2702 | { | |
2703 | /* Note that we don't forbid "*@*", only those, that lack | |
2704 | ** some crucial parts, which can be seen as a typo. --Beeth */ | |
2705 | i = 1; | |
2706 | } | |
2707 | #ifdef KLINE | |
2708 | badkline: | |
2709 | #endif | |
2710 | if (i || !host) | |
2711 | { | |
2712 | /* error */ | |
2713 | if (!IsPerson(sptr)) | |
2714 | { | |
2715 | sendto_one(sptr, ":%s NOTICE %s " | |
2716 | ":T/KLINE: Incorrect format", | |
2717 | ME, parv[0]); | |
2718 | return exit_client(cptr, cptr, &me, | |
2719 | "T/KLINE: Incorrect format"); | |
2720 | } | |
2721 | sendto_one(sptr, ":%s NOTICE %s :%sKLINE: Incorrect format", | |
2722 | ME, parv[0], tkline?"T":""); | |
2723 | return 2; | |
2724 | } | |
2725 | ||
2726 | /* All seems fine. */ | |
2727 | if (*user == '=') | |
2728 | { | |
2729 | status = tkline ? CONF_TOTHERKILL : CONF_OTHERKILL; | |
2730 | user++; | |
2731 | } | |
2732 | *host++ = '\0'; | |
2733 | #ifdef INET6 | |
2734 | host = ipv6_convert(host); | |
2735 | #endif | |
2736 | if (strlen(reason) > TOPICLEN) | |
2737 | { | |
2738 | reason[TOPICLEN] = '\0'; | |
2739 | } | |
2740 | ||
2741 | #ifdef KLINE | |
2742 | if (!tkline) | |
2743 | { | |
2744 | int kfd, ksize, kret; | |
2745 | char kbuf[2*BUFSIZE]; | |
2746 | char *utmp, *htmp, *rtmp; | |
2747 | ||
2748 | if (!strcmp(KLINE_PATH, IRCDCONF_PATH)) | |
2749 | { | |
2750 | sendto_flag(SCH_ERROR, | |
2751 | "Invalid kline configuration file."); | |
2752 | return MAXPENALTY; | |
2753 | } | |
2754 | utmp = strchr(user, IRCDCONF_DELIMITER); | |
2755 | htmp = strchr(host, IRCDCONF_DELIMITER); | |
2756 | rtmp = strchr(reason, IRCDCONF_DELIMITER); | |
2757 | if (utmp || htmp || rtmp) | |
2758 | { | |
2759 | /* Too lazy to copy it here. --B. */ | |
2760 | i = 1; | |
2761 | goto badkline; | |
2762 | } | |
2763 | ||
2764 | kfd = open(KLINE_PATH, O_WRONLY|O_APPEND|O_NDELAY); | |
2765 | if (kfd < 0) | |
2766 | { | |
2767 | sendto_flag(SCH_ERROR, | |
2768 | "Cannot open kline configuration file."); | |
2769 | return MAXPENALTY; | |
2770 | } | |
2771 | ksize = snprintf(kbuf, sizeof(kbuf), | |
2772 | "%c%c%s%c%s%c%s%c0%c #%s%s%s%s%s#%d\n", | |
2773 | (status == CONF_OTHERKILL ? 'k' : 'K'), | |
2774 | IRCDCONF_DELIMITER, host, IRCDCONF_DELIMITER, reason, | |
2775 | IRCDCONF_DELIMITER, user, IRCDCONF_DELIMITER, | |
2776 | IRCDCONF_DELIMITER, sptr->name, | |
2777 | sptr->user ? "!" : "", | |
2778 | sptr->user ? sptr->user->username : "", | |
2779 | sptr->user ? "@" : "", | |
2780 | sptr->user ? sptr->user->host : "", (int)timeofday); | |
2781 | kret = write(kfd, kbuf, ksize); | |
2782 | close(kfd); | |
2783 | if (kret != ksize) | |
2784 | { | |
2785 | sendto_flag(SCH_ERROR, "Error writing (%d!=%d) " | |
2786 | "to kline configuration file.", kret, ksize); | |
2787 | sendto_one(sptr, ":%s NOTICE %s :KLINE: error writing " | |
2788 | "(%d!=%d) to kline configuration file", | |
2789 | ME, parv[0], kret, ksize); | |
2790 | return MAXPENALTY; | |
2791 | } | |
2792 | } | |
2793 | #endif /* KLINE */ | |
2794 | ||
2795 | /* All parameters are now sane. Do the stuff. */ | |
2796 | do_kline(tkline, parv[0], time, user, host, reason, status); | |
2797 | ||
2798 | return 1; | |
2799 | } | |
2800 | #endif /* TKLINE || KLINE */ | |
2801 | ||
2802 | #ifdef KLINE | |
2803 | int m_kline(aClient *cptr, aClient *sptr, int parc, char **parv) | |
2804 | { | |
2805 | if (!is_allowed(sptr, ACL_KLINE)) | |
2806 | return m_nopriv(cptr, sptr, parc, parv); | |
2807 | return prep_kline(0, cptr, sptr, parc, parv); | |
2808 | } | |
2809 | #endif | |
2810 | ||
2811 | #ifdef TKLINE | |
2812 | int m_tkline(aClient *cptr, aClient *sptr, int parc, char **parv) | |
2813 | { | |
2814 | if (!is_allowed(sptr, ACL_TKLINE)) | |
2815 | return m_nopriv(cptr, sptr, parc, parv); | |
2816 | return prep_kline(1, cptr, sptr, parc, parv); | |
2817 | } | |
2818 | ||
2819 | int m_untkline(aClient *cptr, aClient *sptr, int parc, char **parv) | |
2820 | { | |
2821 | aConfItem *tmp, *prev; | |
2822 | char *user, *host; | |
2823 | int deleted = 0; | |
2824 | ||
2825 | if (!is_allowed(sptr, ACL_UNTKLINE)) | |
2826 | return m_nopriv(cptr, sptr, parc, parv); | |
2827 | ||
2828 | user = parv[1]; | |
2829 | host = strchr(user, '@'); | |
2830 | if (!host) | |
2831 | { | |
2832 | /* error */ | |
2833 | if (!IsPerson(sptr)) | |
2834 | { | |
2835 | sendto_one(sptr, ":%s NOTICE %s " | |
2836 | ":UNTKLINE: Incorrect format", | |
2837 | ME, parv[0]); | |
2838 | return exit_client(cptr, cptr, &me, | |
2839 | "UNTKLINE: Incorrect format"); | |
2840 | } | |
2841 | sendto_one(sptr, ":%s NOTICE %s :UNTKLINE: Incorrect format", | |
2842 | ME, parv[0]); | |
2843 | return 2; | |
2844 | } | |
2845 | if (*user == '=') | |
2846 | { | |
2847 | user++; | |
2848 | } | |
2849 | *host++ = '\0'; | |
2850 | ||
2851 | for (prev = tkconf, tmp = tkconf; tmp; tmp = tmp->next) | |
2852 | { | |
2853 | if (0==strcasecmp(tmp->host, host) && | |
2854 | 0==strcasecmp(tmp->name, user)) | |
2855 | { | |
2856 | if (tmp == tkconf) | |
2857 | tkconf = tmp->next; | |
2858 | else | |
2859 | prev->next = tmp->next; | |
2860 | free_conf(tmp); | |
2861 | deleted = 1; | |
2862 | break; | |
2863 | } | |
2864 | prev = tmp; | |
2865 | } | |
2866 | ||
2867 | if (deleted) | |
2868 | { | |
2869 | sendto_flag(SCH_TKILL, "UNTKLINE %s@%s by %s", | |
2870 | user, host, parv[0]); | |
2871 | } | |
2872 | return 1; | |
2873 | } | |
2874 | ||
2875 | time_t tkline_expire(int all) | |
2876 | { | |
2877 | aConfItem *tmp = NULL, *tmp2 = tkconf, *prev = tkconf; | |
2878 | time_t min = 0; | |
2879 | ||
2880 | while ((tmp = tmp2)) | |
2881 | { | |
2882 | tmp2 = tmp->next; | |
2883 | if (all || tmp->hold <= timeofday) | |
2884 | { | |
2885 | if (tmp == tkconf) | |
2886 | tkconf = tmp->next; | |
2887 | else | |
2888 | prev->next = tmp->next; | |
2889 | free_conf(tmp); | |
2890 | continue; | |
2891 | } | |
2892 | if (min == 0 || tmp->hold < min) | |
2893 | { | |
2894 | min = tmp->hold; | |
2895 | } | |
2896 | prev = tmp; | |
2897 | } | |
2898 | if (min && min < nexttkexpire + 60) | |
2899 | min = nexttkexpire + 60; | |
2900 | return min; | |
2901 | } | |
2902 | #endif /* TKLINE */ |