/[svn]/ircd-hybrid/src/client.c
ViewVC logotype

Contents of /ircd-hybrid/src/client.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 758 - (show annotations)
Fri Aug 18 14:26:11 2006 UTC (14 years, 1 month ago) by michael
File MIME type: text/x-chdr
File size: 44121 byte(s)
- Fixed ban cache as reported by ThaPrince

1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * client.c: Controls clients.
4 *
5 * Copyright (C) 2002 by the past and present ircd coders, and others.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * $Id$
23 */
24
25 #include "stdinc.h"
26 #include "conf/conf.h"
27 #include "client.h"
28 #include "channel.h"
29 #include "channel_mode.h"
30 #include "common.h"
31 #include "hash.h"
32 #include "ircd.h"
33 #include "numeric.h"
34 #include "packet.h"
35 #include "s_auth.h"
36 #include "parse_aline.h"
37 #include "s_serv.h"
38 #include "send.h"
39 #include "whowas.h"
40 #include "s_user.h"
41 #include "listener.h"
42 #include "userhost.h"
43 #include "watch.h"
44
45 dlink_list listing_client_list = { NULL, NULL, 0 };
46 /* Pointer to beginning of Client list */
47 dlink_list global_client_list = {NULL, NULL, 0};
48 /* unknown/client pointer lists */
49 dlink_list unknown_list = {NULL, NULL, 0};
50 dlink_list local_client_list = {NULL, NULL, 0};
51 dlink_list serv_list = {NULL, NULL, 0};
52 dlink_list global_serv_list = {NULL, NULL, 0};
53 dlink_list oper_list = {NULL, NULL, 0};
54
55 static EVH check_pings;
56
57 static BlockHeap *client_heap = NULL;
58 static BlockHeap *lclient_heap = NULL;
59
60 static dlink_list dead_list = { NULL, NULL, 0};
61 static dlink_list abort_list = { NULL, NULL, 0};
62
63 static dlink_node *eac_next; /* next aborted client to exit */
64
65 static void check_pings_list(dlink_list *);
66 static void check_unknowns_list(void);
67 static void close_connection(struct Client *);
68 static void ban_them(struct Client *client_p, const char *, const char *);
69 static void del_all_accepts(struct Client *);
70
71 /* init_client()
72 *
73 * inputs - NONE
74 * output - NONE
75 * side effects - initialize client free memory
76 */
77 void
78 init_client(void)
79 {
80 /* start off the check ping event .. -- adrian
81 * Every 30 seconds is plenty -- db
82 */
83 client_heap = BlockHeapCreate("client", sizeof(struct Client), CLIENT_HEAP_SIZE);
84 lclient_heap = BlockHeapCreate("local client", sizeof(struct LocalUser), LCLIENT_HEAP_SIZE);
85 eventAdd("check_pings", check_pings, NULL, 5);
86 }
87
88 /*
89 * make_client - create a new Client struct and set it to initial state.
90 *
91 * from == NULL, create local client (a client connected
92 * to a socket).
93 * WARNING: This leaves the client in a dangerous
94 * state where fd == -1, dead flag is not set and
95 * the client is on the unknown_list; therefore,
96 * the first thing to do after calling make_client(NULL)
97 * is setting fd to something reasonable. -adx
98 *
99 * from, create remote client (behind a socket
100 * associated with the client defined by
101 * 'from'). ('from' is a local client!!).
102 */
103 struct Client *
104 make_client(struct Client *from)
105 {
106 struct Client *client_p = BlockHeapAlloc(client_heap);
107
108 if (from == NULL)
109 {
110 client_p->from = client_p; /* 'from' of local client is self! */
111 client_p->since = client_p->lasttime = client_p->firsttime = CurrentTime;
112
113 client_p->localClient = BlockHeapAlloc(lclient_heap);
114 client_p->localClient->class = default_class;
115 client_p->localClient->registration = REG_INIT;
116 /* as good a place as any... */
117 dlinkAdd(client_p, &client_p->localClient->lclient_node, &unknown_list);
118 }
119 else
120 client_p->from = from;
121
122 client_p->hnext = client_p;
123 client_p->status = STAT_UNKNOWN;
124 strcpy(client_p->username, "unknown");
125
126 return client_p;
127 }
128
129 /*
130 * free_client
131 *
132 * inputs - pointer to client
133 * output - NONE
134 * side effects - client pointed to has its memory freed
135 */
136 static void
137 free_client(struct Client *client_p)
138 {
139 assert(client_p != NULL);
140 assert(client_p != &me);
141 assert(client_p->hnext == client_p);
142 assert(client_p->channel.head == NULL);
143 assert(dlink_list_length(&client_p->channel) == 0);
144
145 MyFree(client_p->away);
146 MyFree(client_p->serv);
147
148 if (MyConnect(client_p))
149 {
150 assert(dlink_list_length(&client_p->localClient->invited) == 0);
151 assert(client_p->localClient->invited.head == NULL);
152 assert(IsClosing(client_p) && IsDead(client_p));
153
154 MyFree(client_p->localClient->response);
155 MyFree(client_p->localClient->auth_oper);
156
157 /*
158 * clean up extra sockets from P-lines which have been discarded.
159 */
160 if (client_p->localClient->listener)
161 {
162 assert(0 < client_p->localClient->listener->ref_count);
163 if (0 == --client_p->localClient->listener->ref_count &&
164 !client_p->localClient->listener->active)
165 free_listener(client_p->localClient->listener);
166 }
167
168 dbuf_clear(&client_p->localClient->buf_recvq);
169 dbuf_clear(&client_p->localClient->buf_sendq);
170
171 BlockHeapFree(lclient_heap, client_p->localClient);
172 }
173
174 BlockHeapFree(client_heap, client_p);
175 }
176
177 /*
178 * check_pings - go through the local client list and check activity
179 * kill off stuff that should die
180 *
181 * inputs - NOT USED (from event)
182 * output - next time_t when check_pings() should be called again
183 * side effects -
184 *
185 *
186 * A PING can be sent to clients as necessary.
187 *
188 * Client/Server ping outs are handled.
189 */
190
191 /*
192 * Addon from adrian. We used to call this after nextping seconds,
193 * however I've changed it to run once a second. This is only for
194 * PING timeouts, not K/etc-line checks (thanks dianora!). Having it
195 * run once a second makes life a lot easier - when a new client connects
196 * and they need a ping in 4 seconds, if nextping was set to 20 seconds
197 * we end up waiting 20 seconds. This is stupid. :-)
198 * I will optimise (hah!) check_pings() once I've finished working on
199 * tidying up other network IO evilnesses.
200 * -- adrian
201 */
202
203 static void
204 check_pings(void *notused)
205 {
206 check_pings_list(&local_client_list);
207 check_pings_list(&serv_list);
208 check_unknowns_list();
209 }
210
211 /* check_pings_list()
212 *
213 * inputs - pointer to list to check
214 * output - NONE
215 * side effects -
216 */
217 static void
218 check_pings_list(dlink_list *list)
219 {
220 char scratch[32]; // way too generous but...
221 struct Client *client_p; // current local client_p being examined
222 int ping, pingwarn; // ping time value from client
223 dlink_node *ptr, *next_ptr;
224
225 DLINK_FOREACH_SAFE(ptr, next_ptr, list->head)
226 {
227 client_p = ptr->data;
228
229 /*
230 * Note: No need to notify opers here. It's
231 * already done when "FLAGS_DEADSOCKET" is set.
232 */
233 if (IsDead(client_p))
234 {
235 // Ignore it, it's been exited already
236 continue;
237 }
238
239 if (client_p->localClient->reject_delay > 0)
240 {
241 if (client_p->localClient->reject_delay <= CurrentTime)
242 exit_client(client_p, &me, "Rejected");
243 continue;
244 }
245
246 if (GlobalSetOptions.idletime && IsClient(client_p))
247 {
248 if (!IsExemptKline(client_p) && !IsIdlelined(client_p) &&
249 !IsOper(client_p) && ((CurrentTime - client_p->localClient->last) >
250 GlobalSetOptions.idletime))
251 {
252 struct KillConf *conf = MyMalloc(sizeof(struct KillConf));
253
254 conf->access.type = acb_type_kline;
255 DupString(conf->access.user, client_p->username);
256 DupString(conf->access.host, client_p->host);
257 conf->access.expires = CurrentTime + 60;
258 DupString(conf->reason, "idle exceeder");
259 add_access_conf(&conf->access);
260
261 sendto_realops_flags(UMODE_ALL, L_ALL,
262 "Idle time limit exceeded for %s - temp k-lining",
263 get_client_name(client_p, HIDE_IP));
264 exit_client(client_p, &me, conf->reason);
265 continue;
266 }
267 }
268
269 ping = client_p->localClient->class->ping_time;
270 pingwarn = client_p->localClient->class->ping_warning;
271
272 if (ping < CurrentTime - client_p->lasttime)
273 {
274 if (!IsPingSent(client_p))
275 {
276 /*
277 * if we havent PINGed the connection and we havent
278 * heard from it in a while, PING it to make sure
279 * it is still alive.
280 */
281 SetPingSent(client_p);
282 ClearPingWarning(client_p);
283 client_p->lasttime = CurrentTime - ping;
284 sendto_one(client_p, "PING :%s", ID_or_name(&me, client_p));
285 }
286 else
287 {
288 if (CurrentTime - client_p->lasttime >= 2 * ping)
289 {
290 /*
291 * If the client/server hasn't talked to us in 2*ping seconds
292 * and it has a ping time, then close its connection.
293 */
294 if (IsServer(client_p) || IsHandshake(client_p))
295 {
296 sendto_realops_flags(UMODE_ALL, L_ADMIN,
297 "No response from %s, closing link",
298 get_client_name(client_p, HIDE_IP));
299 sendto_realops_flags(UMODE_ALL, L_OPER,
300 "No response from %s, closing link",
301 get_client_name(client_p, MASK_IP));
302 ilog(L_NOTICE, "No response from %s, closing link",
303 get_client_name(client_p, HIDE_IP));
304 }
305 ircsprintf(scratch, "Ping timeout: %d seconds",
306 (int)(CurrentTime - client_p->lasttime));
307
308 exit_client(client_p, &me, scratch);
309 }
310 else if (!IsPingWarning(client_p) && pingwarn > 0 &&
311 (IsServer(client_p) || IsHandshake(client_p)) &&
312 CurrentTime - client_p->lasttime >= ping + pingwarn)
313 {
314 /*
315 * If the server hasn't replied in pingwarn seconds after sending
316 * the PING, notify the opers so that they are aware of the problem.
317 */
318 SetPingWarning(client_p);
319 sendto_realops_flags(UMODE_ALL, L_ADMIN,
320 "Warning, no response from %s in %d seconds",
321 get_client_name(client_p, HIDE_IP), pingwarn);
322 sendto_realops_flags(UMODE_ALL, L_OPER,
323 "Warning, no response from %s in %d seconds",
324 get_client_name(client_p, MASK_IP), pingwarn);
325 ilog(L_NOTICE, "No response from %s in %d seconds",
326 get_client_name(client_p, HIDE_IP), pingwarn);
327 }
328 }
329 }
330 }
331 }
332
333 /* check_unknowns_list()
334 *
335 * inputs - pointer to list of unknown clients
336 * output - NONE
337 * side effects - unknown clients get marked for termination after n seconds
338 */
339 static void
340 check_unknowns_list(void)
341 {
342 dlink_node *ptr = NULL, *next_ptr = NULL;
343
344 DLINK_FOREACH_SAFE(ptr, next_ptr, unknown_list.head)
345 {
346 struct Client *client_p = ptr->data;
347
348 if (client_p->localClient->reject_delay > 0)
349 {
350 if (client_p->localClient->reject_delay <= CurrentTime)
351 exit_client(client_p, &me, "Rejected");
352 continue;
353 }
354
355 /*
356 * Check UNKNOWN connections - if they have been in this state
357 * for > 30s, close them.
358 */
359 if (IsAuthFinished(client_p) && (CurrentTime - client_p->firsttime) > 30)
360 exit_client(client_p, &me, "Registration timed out");
361 }
362 }
363
364 /* check_conf_klines()
365 *
366 * inputs - NONE
367 * output - NONE
368 * side effects - Check all connections for a pending kline against the
369 * client, exit the client if a kline matches.
370 */
371 void
372 check_conf_klines(void)
373 {
374 struct Client *client_p = NULL; // current local client_p being examined
375 dlink_node *ptr, *next_ptr;
376 struct DenyConf *conf;
377 char *type, *reason;
378
379 DLINK_FOREACH_SAFE(ptr, next_ptr, local_client_list.head)
380 {
381 client_p = ptr->data;
382
383 // If a client is already being exited
384 if (IsDead(client_p) || !IsClient(client_p))
385 continue;
386
387 // if there is a returned type then kill it
388 if (execute_callback(is_client_banned, client_p, &type, &reason))
389 {
390 ban_them(client_p, type, reason);
391 continue; // and go examine next fd/client_p
392 }
393 }
394
395 // also check the unknowns list for new dlines
396 DLINK_FOREACH_SAFE(ptr, next_ptr, unknown_list.head)
397 {
398 client_p = ptr->data;
399
400 if ((conf = find_dline(&client_p->localClient->ip)))
401 exit_client(client_p, &me, "D-lined");
402 }
403 }
404
405 /*
406 * ban_them
407 *
408 * inputs - pointer to client to ban
409 * - type of ban (e.g. K-line, X-line)
410 * - ban reason
411 * output - NONE
412 * side effects - given client_p is banned
413 */
414 static void
415 ban_them(struct Client *client_p, const char *type, const char *reason)
416 {
417 const char *user_reason; // What is sent to user
418 const char *channel_reason; // What is sent to channel
419
420 user_reason = (EmptyString(reason) || !General.kline_with_reason) ?
421 type : reason;
422 channel_reason = General.kline_reason ? General.kline_reason : user_reason;
423
424 sendto_realops_flags(UMODE_ALL, L_ALL, "%s active for %s",
425 type, get_client_name(client_p, HIDE_IP));
426
427 if (IsClient(client_p))
428 sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP),
429 me.name, client_p->name, user_reason);
430
431 exit_client(client_p, &me, channel_reason);
432 }
433
434 /* find_person()
435 *
436 * inputs - pointer to name
437 * output - return client pointer
438 * side effects - find person by (nick)name
439 */
440 struct Client *
441 find_person(const struct Client *source_p, const char *name)
442 {
443 struct Client *target_p = NULL;
444
445 if (IsDigit(*name) && IsServer(source_p->from))
446 target_p = hash_find_id(name);
447 else
448 target_p = find_client(name);
449
450 return (target_p && IsClient(target_p)) ? target_p : NULL;
451 }
452
453 /*
454 * find_chasing - find the client structure for a nick name (user)
455 * using history mechanism if necessary. If the client is not found,
456 * an error message (NO SUCH NICK) is generated. If the client was found
457 * through the history, chasing will be 1 and otherwise 0.
458 */
459 struct Client *
460 find_chasing(struct Client *source_p, const char *user, int *chasing)
461 {
462 struct Client *who = find_person(source_p, user);
463
464 if (chasing)
465 *chasing = 0;
466
467 if (who)
468 return who;
469
470 if (IsDigit(*user))
471 return NULL;
472
473 who = whowas_get_history(user, (time_t) General.kill_chase_time_limit);
474 if (!who)
475 {
476 sendto_one(source_p, form_str(ERR_NOSUCHNICK),
477 me.name, source_p->name, user);
478 return NULL;
479 }
480
481 if (chasing)
482 *chasing = 1;
483
484 return who;
485 }
486
487 /*
488 * get_client_name - Return the name of the client
489 * for various tracking and
490 * admin purposes. The main purpose of this function is to
491 * return the "socket host" name of the client, if that
492 * differs from the advertised name (other than case).
493 * But, this can be used to any client structure.
494 *
495 * NOTE 1:
496 * Watch out the allocation of "nbuf", if either source_p->name
497 * or source_p->sockhost gets changed into pointers instead of
498 * directly allocated within the structure...
499 *
500 * NOTE 2:
501 * Function return either a pointer to the structure (source_p) or
502 * to internal buffer (nbuf). *NEVER* use the returned pointer
503 * to modify what it points!!!
504 */
505 const char *
506 get_client_name(const struct Client *client, int showip)
507 {
508 static char nbuf[HOSTLEN * 2 + USERLEN + 5];
509
510 assert(client != NULL);
511
512 if (!irccmp(client->name, client->host))
513 return client->name;
514
515 if (ServerHide.hide_server_ips)
516 if (IsServer(client) || IsConnecting(client) || IsHandshake(client))
517 showip = MASK_IP;
518
519 if (General.hide_spoof_ips)
520 if (showip == SHOW_IP && IsIPSpoof(client))
521 showip = MASK_IP;
522
523 /* And finally, let's get the host information, ip or name */
524 switch (showip)
525 {
526 case SHOW_IP:
527 if (client->sockhost[0] != '\0' || irccmp(client->sockhost, "0") != 0)
528 ircsprintf(nbuf, "%s[%s@%s]", client->name, client->username,
529 client->sockhost);
530 else
531 ircsprintf(nbuf, "%s[%s@%s]", client->name, client->username,
532 client->host);
533 break;
534 case MASK_IP:
535 ircsprintf(nbuf, "%s[%s@255.255.255.255]", client->name,
536 client->username);
537 break;
538 default:
539 ircsprintf(nbuf, "%s[%s@%s]", client->name, client->username,
540 client->host);
541 }
542
543 return nbuf;
544 }
545
546 void
547 free_exited_clients(void)
548 {
549 dlink_node *ptr, *next;
550
551 DLINK_FOREACH_SAFE(ptr, next, dead_list.head)
552 {
553 free_client(ptr->data);
554 dlinkDelete(ptr, &dead_list);
555 free_dlink_node(ptr);
556 }
557 }
558
559 /*
560 * Exit one client, local or remote. Assuming all dependents have
561 * been already removed, and socket closed for local client.
562 *
563 * The only messages generated are QUITs on channels.
564 */
565 static void
566 exit_one_client(struct Client *source_p, const char *quitmsg)
567 {
568 dlink_node *lp = NULL, *next_lp = NULL;
569
570 assert(!IsMe(source_p) && (source_p != &me));
571
572 if (IsClient(source_p))
573 {
574 assert(source_p->servptr);
575 assert(source_p->servptr->serv);
576
577 --Count.total;
578 if (IsOper(source_p))
579 --Count.oper;
580 if (IsInvisible(source_p))
581 --Count.invisi;
582
583 dlinkDelete(&source_p->lnode, &source_p->servptr->serv->client_list);
584
585 /*
586 * If a person is on a channel, send a QUIT notice to every
587 * client (person) on the same channel (so that the client
588 * can show the "**signoff" message). (Note: The notice is
589 * to the local clients *only*)
590 */
591 sendto_common_channels_local(source_p, 0, ":%s!%s@%s QUIT :%s",
592 source_p->name, source_p->username,
593 source_p->host, quitmsg);
594 DLINK_FOREACH_SAFE(lp, next_lp, source_p->channel.head)
595 remove_user_from_channel(lp->data);
596
597 whowas_add_history(source_p, 0);
598 whowas_off_history(source_p);
599
600 assert(source_p->whowas.head == NULL);
601 assert(source_p->whowas.tail == NULL);
602
603 watch_check_hash(source_p, RPL_LOGOFF);
604
605 /* Do local vs. remote processing here */
606 if (MyConnect(source_p))
607 {
608 /* Clean up invitefield */
609 DLINK_FOREACH_SAFE(lp, next_lp, source_p->localClient->invited.head)
610 del_invite(lp->data, source_p);
611
612 /* Clean up allow lists */
613 del_all_accepts(source_p);
614 }
615 }
616 else if (IsServer(source_p))
617 {
618 sendto_realops_flags(UMODE_EXTERNAL, L_ALL,
619 "Server %s split from %s",
620 source_p->name, source_p->servptr->name);
621
622 dlinkDelete(&source_p->lnode, &source_p->servptr->serv->server_list);
623
624 if ((lp = dlinkFindDelete(&global_serv_list, source_p)) != NULL)
625 free_dlink_node(lp);
626 }
627
628 if (splitchecking && !splitmode)
629 check_splitmode(NULL);
630
631 /* Remove source_p from the client lists */
632 if (HasID(source_p))
633 hash_del_id(source_p);
634 if (source_p->name[0])
635 hash_del_client(source_p);
636
637 if (IsUserHostIp(source_p))
638 delete_user_host(source_p->username, source_p->host, !MyConnect(source_p));
639
640 /* remove from global client list
641 * NOTE: source_p->node.next cannot be NULL if the client is added
642 * to global_client_list (there is always &me at its end)
643 */
644 if (source_p != NULL && source_p->node.next != NULL)
645 dlinkDelete(&source_p->node, &global_client_list);
646
647 /* Check to see if the client isn't already on the dead list */
648 assert(dlinkFind(&dead_list, source_p) == NULL);
649
650 /* add to dead client dlist */
651 SetDead(source_p);
652 dlinkAdd(source_p, make_dlink_node(), &dead_list);
653 }
654
655 /* Recursively send QUITs and SQUITs for source_p and all its dependent clients
656 * and servers to those servers that need them. A server needs the client
657 * QUITs if it can't figure them out from the SQUIT (ie pre-TS4) or if it
658 * isn't getting the SQUIT because of @#(*&@)# hostmasking. With TS4, once
659 * a link gets a SQUIT, it doesn't need any QUIT/SQUITs for clients depending
660 * on that one -orabidoo
661 *
662 * This is now called on each local server -adx
663 */
664 static void
665 recurse_send_quits(struct Client *original_source_p, struct Client *source_p,
666 struct Client *from, struct Client *to, const char *comment,
667 const char *splitstr, const char *myname)
668 {
669 dlink_node *ptr, *next;
670 struct Client *target_p;
671 int hidden = match(myname, source_p->name);
672
673 assert(to != source_p); /* should be already removed from serv_list */
674
675 /* If this server can handle quit storm (QS) removal
676 * of dependents, just send the SQUIT
677 *
678 * Always check *all* dependent servers if some of them are
679 * hidden behind fakename. If so, send out the QUITs -adx
680 */
681 if (hidden || !IsCapable(to, CAP_QS))
682 {
683 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->client_list.head)
684 {
685 target_p = ptr->data;
686 sendto_one(to, ":%s QUIT :%s", target_p->name, splitstr);
687 }
688 }
689
690 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->server_list.head)
691 recurse_send_quits(original_source_p, ptr->data, from, to,
692 comment, splitstr, myname);
693
694 if (!hidden && ((source_p == original_source_p && to != from) ||
695 !IsCapable(to, CAP_QS)))
696 {
697 /* don't use a prefix here - we have to be 100% sure the message
698 * will be accepted without Unknown prefix etc.. */
699 sendto_one(to, "SQUIT %s :%s", ID_or_name(source_p, to), comment);
700 }
701 }
702
703 /*
704 * Remove all clients that depend on source_p; assumes all (S)QUITs have
705 * already been sent. we make sure to exit a server's dependent clients
706 * and servers before the server itself; exit_one_client takes care of
707 * actually removing things off llists. tweaked from +CSr31 -orabidoo
708 */
709 static void
710 recurse_remove_clients(struct Client *source_p, const char *quitmsg)
711 {
712 dlink_node *ptr = NULL, *next = NULL;
713
714 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->client_list.head)
715 exit_one_client(ptr->data, quitmsg);
716
717 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->server_list.head)
718 {
719 recurse_remove_clients(ptr->data, quitmsg);
720 exit_one_client(ptr->data, quitmsg);
721 }
722 }
723
724 /*
725 ** Remove *everything* that depends on source_p, from all lists, and sending
726 ** all necessary QUITs and SQUITs. source_p itself is still on the lists,
727 ** and its SQUITs have been sent except for the upstream one -orabidoo
728 */
729 static void
730 remove_dependents(struct Client *source_p, struct Client *from,
731 const char *comment, const char *splitstr)
732 {
733 struct Client *to;
734 struct ConnectConf *conf;
735 static char myname[HOSTLEN+1];
736 dlink_node *ptr;
737
738 DLINK_FOREACH(ptr, serv_list.head)
739 {
740 to = ptr->data;
741
742 if ((conf = to->serv->sconf) != NULL)
743 strlcpy(myname, my_name_for_link(conf), sizeof(myname));
744 else
745 strlcpy(myname, me.name, sizeof(myname));
746 recurse_send_quits(source_p, source_p, from, to,
747 comment, splitstr, myname);
748 }
749
750 recurse_remove_clients(source_p, splitstr);
751 }
752
753 /*
754 * exit_client - exit a client of any type. Generally, you can use
755 * this on any struct Client, regardless of its state.
756 *
757 * Note, you shouldn't exit remote _users_ without first doing
758 * SetKilled and propagating a kill or similar message. However,
759 * it is perfectly correct to call exit_client to force a _server_
760 * quit (either local or remote one).
761 *
762 * inputs: - a client pointer that is going to be exited
763 * - for servers, the second argument is a pointer to who
764 * is firing the server. This side won't get any generated
765 * messages. NEVER NULL!
766 * output: none
767 * side effects: the client is delinked from all lists, disconnected,
768 * and the rest of IRC network is notified of the exit.
769 * Client memory is scheduled to be freed
770 */
771 void
772 exit_client(struct Client *source_p, struct Client *from, const char *comment)
773 {
774 dlink_node *m = NULL;
775
776 if (MyConnect(source_p))
777 {
778 /*
779 * DO NOT REMOVE. exit_client can be called twice after a failed
780 * read/write.
781 */
782 if (IsClosing(source_p))
783 return;
784
785 SetClosing(source_p);
786
787 if (IsIpHash(source_p))
788 remove_one_ip(&source_p->localClient->ip);
789
790 delete_auth(source_p);
791
792 if (IsClient(source_p))
793 {
794 --Count.local;
795
796 if (IsOper(source_p))
797 if ((m = dlinkFindDelete(&oper_list, source_p)) != NULL)
798 free_dlink_node(m);
799
800 dlinkDelete(&source_p->localClient->lclient_node, &local_client_list);
801
802 if (source_p->localClient->list_task != NULL)
803 free_list_task(source_p->localClient->list_task, source_p);
804
805 watch_del_watch_list(source_p);
806 sendto_realops_flags(UMODE_CCONN, L_ALL,
807 "Client exiting: %s (%s@%s) [%s] [%s]",
808 source_p->name, source_p->username, source_p->host, comment,
809 General.hide_spoof_ips && IsIPSpoof(source_p) ?
810 "255.255.255.255" : source_p->sockhost);
811 }
812 else if (IsServer(source_p))
813 {
814 if ((m = dlinkFindDelete(&serv_list, source_p)) != NULL)
815 unset_chcap_usage_counts(source_p);
816
817 --Count.myserver;
818 }
819 else
820 {
821 /*
822 * This source_p could have status of one of STAT_UNKNOWN,
823 * STAT_CONNECTING or STAT_HANDSHAKE
824 * all of which are lumped together into unknown_list
825 *
826 * In all above cases IsRegistered() will not be true.
827 */
828 assert(!IsRegistered(source_p));
829 dlinkDelete(&source_p->localClient->lclient_node, &unknown_list);
830 }
831
832 log_user_exit(source_p);
833
834 if (!IsDead(source_p))
835 {
836 if (IsServer(source_p))
837 {
838 /* for them, we are exiting the network */
839 sendto_one(source_p, ":%s SQUIT %s :%s",
840 ID_or_name(from, source_p), me.name, comment);
841 }
842
843 sendto_one(source_p, "ERROR :Closing Link: %s (%s)",
844 source_p->host, comment);
845 }
846
847 /*
848 * Close the Client connection first and mark it so that no
849 * messages are attempted to send to it. Remember it makes
850 * source_p->from == NULL.
851 */
852 close_connection(source_p);
853 }
854
855 if (IsClient(source_p) && !IsKilled(source_p))
856 {
857 sendto_server(from->from, source_p, NULL, CAP_TS6, NOCAPS,
858 ":%s QUIT :%s", ID(source_p), comment);
859 sendto_server(from->from, source_p, NULL, NOCAPS, CAP_TS6,
860 ":%s QUIT :%s", source_p->name, comment);
861 }
862 else if (IsServer(source_p))
863 {
864 char splitstr[HOSTLEN + HOSTLEN + 2];
865
866 /* This shouldn't ever happen */
867 assert(source_p->serv != NULL && source_p->servptr != NULL);
868
869 if (ServerHide.hide_servers)
870 /*
871 * set netsplit message to "*.net *.split" to still show
872 * that it's a split, but hide the servers splitting
873 */
874 strcpy(splitstr, "*.net *.split");
875 else
876 snprintf(splitstr, sizeof(splitstr), "%s %s",
877 source_p->servptr->name, source_p->name);
878
879 remove_dependents(source_p, from->from, comment, splitstr);
880
881 if (MyConnect(source_p))
882 {
883 sendto_realops_flags(UMODE_ALL, L_ALL,
884 "%s was connected for %d seconds. %llu/%llu sendK/recvK.",
885 source_p->name, (int)(CurrentTime - source_p->firsttime),
886 source_p->localClient->send.bytes >> 10,
887 source_p->localClient->recv.bytes >> 10);
888 ilog(L_NOTICE, "%s was connected for %d seconds. %llu/%llu sendK/recvK.",
889 source_p->name, (int)(CurrentTime - source_p->firsttime),
890 source_p->localClient->send.bytes >> 10,
891 source_p->localClient->recv.bytes >> 10);
892 }
893 }
894
895 /* The client *better* be off all of the lists */
896 assert(dlinkFind(&unknown_list, source_p) == NULL);
897 assert(dlinkFind(&local_client_list, source_p) == NULL);
898 assert(dlinkFind(&serv_list, source_p) == NULL);
899 assert(dlinkFind(&oper_list, source_p) == NULL);
900
901 exit_one_client(source_p, comment);
902 }
903
904 /*
905 * close_connection
906 * Close the physical connection. This function sets client_p->from == NULL.
907 */
908 static void
909 close_connection(struct Client *client_p)
910 {
911 assert(NULL != client_p);
912
913 if (!IsDead(client_p))
914 {
915 /* attempt to flush any pending dbufs. Evil, but .. -- adrian */
916 /* there is still a chance that we might send data to this socket
917 * even if it is marked as blocked (COMM_SELECT_READ handler is called
918 * before COMM_SELECT_WRITE). Let's try, nothing to lose.. -adx
919 */
920 ClearSendqBlocked(client_p);
921 send_queued_write(client_p);
922 }
923
924 if (IsClient(client_p))
925 {
926 ++ServerStats.is_cl;
927 ServerStats.is_cbs += client_p->localClient->send.bytes;
928 ServerStats.is_cbr += client_p->localClient->recv.bytes;
929 ServerStats.is_cti += CurrentTime - client_p->firsttime;
930 }
931 else if (IsServer(client_p))
932 {
933 ++ServerStats.is_sv;
934 ServerStats.is_sbs += client_p->localClient->send.bytes;
935 ServerStats.is_sbr += client_p->localClient->recv.bytes;
936 ServerStats.is_sti += CurrentTime - client_p->firsttime;
937 }
938 else
939 ++ServerStats.is_ni;
940
941 #ifdef HAVE_LIBCRYPTO
942 if (client_p->localClient->fd.ssl)
943 {
944 SSL_set_shutdown(client_p->localClient->fd.ssl, SSL_RECEIVED_SHUTDOWN);
945
946 if (!SSL_shutdown(client_p->localClient->fd.ssl))
947 SSL_shutdown(client_p->localClient->fd.ssl);
948 }
949 #endif
950 if (client_p->localClient->fd.flags.open)
951 fd_close(&client_p->localClient->fd);
952
953 if (HasServlink(client_p))
954 if (client_p->localClient->ctrlfd.flags.open)
955 fd_close(&client_p->localClient->ctrlfd);
956
957 dbuf_clear(&client_p->localClient->buf_sendq);
958 dbuf_clear(&client_p->localClient->buf_recvq);
959
960 MyFree(client_p->localClient->passwd);
961
962 unref_class(client_p->localClient->class);
963 client_p->localClient->class = NULL;
964 if (client_p->serv && client_p->serv->sconf)
965 {
966 unref_link(client_p->serv->sconf);
967 client_p->serv->sconf = NULL;
968 }
969
970 client_p->from = NULL; /* ...this should catch them! >:) --msa */
971 }
972
973 /*
974 * report_error - report an error from an errno.
975 * Record error to log and also send a copy to all *LOCAL* opers online.
976 *
977 * text is a *format* string for outputing error. It must
978 * contain only two '%s', the first will be replaced
979 * by the sockhost from the client_p, and the latter will
980 * be taken from sys_errlist[errno].
981 *
982 * client_p if not NULL, is the *LOCAL* client associated with
983 * the error.
984 *
985 * Cannot use perror() within daemon. stderr is closed in
986 * ircd and cannot be used. And, worse yet, it might have
987 * been reassigned to a normal connection...
988 *
989 * Actually stderr is still there IFF ircd was run with -s --Rodder
990 */
991 void
992 report_error(int level, const char *text, const char *who, int error)
993 {
994 who = (who != NULL) ? who : "";
995
996 sendto_realops_flags(UMODE_DEBUG, level, text, who, strerror(error));
997 log_oper_action(LOG_IOERR_TYPE, NULL, "%s %s %s\n", who, text, strerror(error));
998 ilog(L_ERROR, text, who, strerror(error));
999 }
1000
1001 /*
1002 * dead_link_on_write - report a write error if not already dead,
1003 * mark it as dead then exit it
1004 */
1005 void
1006 dead_link_on_write(struct Client *client_p, int ierrno)
1007 {
1008 dlink_node *ptr;
1009
1010 if (IsDefunct(client_p))
1011 return;
1012
1013 dbuf_clear(&client_p->localClient->buf_recvq);
1014 dbuf_clear(&client_p->localClient->buf_sendq);
1015
1016 assert(dlinkFind(&abort_list, client_p) == NULL);
1017 ptr = make_dlink_node();
1018 /* don't let exit_aborted_clients() finish yet */
1019 dlinkAddTail(client_p, ptr, &abort_list);
1020
1021 if (eac_next == NULL)
1022 eac_next = ptr;
1023
1024 SetDead(client_p); /* You are dead my friend */
1025 }
1026
1027 /*
1028 * dead_link_on_read - report a read error if not already dead,
1029 * mark it as dead then exit it
1030 */
1031 void
1032 dead_link_on_read(struct Client *client_p, int error)
1033 {
1034 char errmsg[IRCD_BUFSIZE];
1035 int current_error;
1036
1037 if (IsDefunct(client_p))
1038 return;
1039
1040 dbuf_clear(&client_p->localClient->buf_recvq);
1041 dbuf_clear(&client_p->localClient->buf_sendq);
1042
1043 current_error = get_sockerr(client_p->localClient->fd.fd);
1044
1045 if (IsServer(client_p) || IsHandshake(client_p))
1046 {
1047 int connected = CurrentTime - client_p->firsttime;
1048
1049 if (error == 0)
1050 {
1051 /* Admins get the real IP */
1052 sendto_realops_flags(UMODE_ALL, L_ADMIN,
1053 "Server %s closed the connection",
1054 get_client_name(client_p, SHOW_IP));
1055
1056 /* Opers get a masked IP */
1057 sendto_realops_flags(UMODE_ALL, L_OPER,
1058 "Server %s closed the connection",
1059 get_client_name(client_p, MASK_IP));
1060
1061 ilog(L_NOTICE, "Server %s closed the connection",
1062 get_client_name(client_p, SHOW_IP));
1063 }
1064 else
1065 {
1066 report_error(L_ADMIN, "Lost connection to %s: %d",
1067 get_client_name(client_p, SHOW_IP), current_error);
1068 report_error(L_OPER, "Lost connection to %s: %d",
1069 get_client_name(client_p, MASK_IP), current_error);
1070 }
1071
1072 sendto_realops_flags(UMODE_ALL, L_ALL,
1073 "%s had been connected for %d day%s, %2d:%02d:%02d",
1074 client_p->name, connected/86400,
1075 (connected/86400 == 1) ? "" : "s",
1076 (connected % 86400) / 3600, (connected % 3600) / 60,
1077 connected % 60);
1078 }
1079
1080 if (error == 0)
1081 strlcpy(errmsg, "Remote host closed the connection",
1082 sizeof(errmsg));
1083 else
1084 ircsprintf(errmsg, "Read error: %s",
1085 strerror(current_error));
1086
1087 exit_client(client_p, &me, errmsg);
1088 }
1089
1090 void
1091 exit_aborted_clients(void)
1092 {
1093 dlink_node *ptr;
1094 struct Client *target_p;
1095 const char *notice;
1096
1097 DLINK_FOREACH_SAFE(ptr, eac_next, abort_list.head)
1098 {
1099 target_p = ptr->data;
1100 eac_next = ptr->next;
1101
1102 if (target_p == NULL)
1103 {
1104 sendto_realops_flags(UMODE_ALL, L_ALL,
1105 "Warning: null client on abort_list!");
1106 dlinkDelete(ptr, &abort_list);
1107 free_dlink_node(ptr);
1108 continue;
1109 }
1110
1111 dlinkDelete(ptr, &abort_list);
1112
1113 if (IsSendQExceeded(target_p))
1114 notice = "Max SendQ exceeded";
1115 else
1116 notice = "Write error: connection closed";
1117
1118 exit_client(target_p, &me, notice);
1119 free_dlink_node(ptr);
1120 }
1121 }
1122
1123 /*
1124 * accept processing, this adds a form of "caller ID" to ircd
1125 *
1126 * If a client puts themselves into "caller ID only" mode,
1127 * only clients that match a client pointer they have put on
1128 * the accept list will be allowed to message them.
1129 *
1130 * Diane Bruce, "Dianora" db@db.net
1131 */
1132
1133 void
1134 del_accept(struct Accept *acceptvar, struct Client *client_p)
1135 {
1136 dlinkDelete(&acceptvar->node, &client_p->localClient->acceptlist);
1137
1138 MyFree(acceptvar->nick);
1139 MyFree(acceptvar->user);
1140 MyFree(acceptvar->host);
1141 MyFree(acceptvar);
1142 }
1143
1144 struct Accept *
1145 find_accept(const char *nick, const char *user,
1146 const char *host, struct Client *client_p, int do_match)
1147 {
1148 dlink_node *ptr = NULL;
1149 /* XXX We wouldn't need that if match() would return 0 on match */
1150 int (*cmpfunc)(const char *, const char *) = do_match ? match : irccmp;
1151
1152 DLINK_FOREACH(ptr, client_p->localClient->acceptlist.head)
1153 {
1154 struct Accept *acceptvar = ptr->data;
1155
1156 if (cmpfunc(acceptvar->nick, nick) == do_match &&
1157 cmpfunc(acceptvar->user, user) == do_match &&
1158 cmpfunc(acceptvar->host, host) == do_match)
1159 return acceptvar;
1160 }
1161
1162 return NULL;
1163 }
1164
1165 /* accept_message()
1166 *
1167 * inputs - pointer to source client
1168 * - pointer to target client
1169 * output - 1 if accept this message 0 if not
1170 * side effects - See if source is on target's allow list
1171 */
1172 int
1173 accept_message(struct Client *source,
1174 struct Client *target)
1175 {
1176 dlink_node *ptr = NULL;
1177
1178 if (source == target || find_accept(source->name, source->username,
1179 source->host, target, 1))
1180 return 1;
1181
1182 if (IsSoftCallerId(target))
1183 DLINK_FOREACH(ptr, target->channel.head)
1184 if (IsMember(source, ((struct Membership *)ptr->data)->chptr))
1185 return 1;
1186
1187 return 0;
1188 }
1189
1190 /* del_all_accepts()
1191 *
1192 * inputs - pointer to exiting client
1193 * output - NONE
1194 * side effects - Walk through given clients acceptlist and remove all entries
1195 */
1196 void
1197 del_all_accepts(struct Client *client_p)
1198 {
1199 dlink_node *ptr = NULL, *next_ptr = NULL;
1200
1201 DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->localClient->acceptlist.head)
1202 del_accept(ptr->data, client_p);
1203 }
1204
1205 /* set_initial_nick()
1206 *
1207 * inputs
1208 * output
1209 * side effects -
1210 *
1211 * This function is only called to set up an initially registering
1212 * client.
1213 */
1214 void
1215 set_initial_nick(struct Client *source_p, const char *nick)
1216 {
1217 char buf[USERLEN + 1];
1218
1219 /* This had to be copied here to avoid problems.. */
1220 source_p->tsinfo = CurrentTime;
1221 source_p->localClient->registration &= ~REG_NEED_NICK;
1222
1223 if (source_p->name[0])
1224 hash_del_client(source_p);
1225
1226 strlcpy(source_p->name, nick, sizeof(source_p->name));
1227 hash_add_client(source_p);
1228
1229 /* fd_desc is long enough */
1230 fd_note(&source_p->localClient->fd, "Nick: %s", nick);
1231
1232 if (!source_p->localClient->registration)
1233 {
1234 strlcpy(buf, source_p->username, sizeof(buf));
1235
1236 /*
1237 * USER already received, now we have NICK.
1238 * *NOTE* For servers "NICK" *must* precede the
1239 * user message (giving USER before NICK is possible
1240 * only for local client connection!). register_user
1241 * may reject the client and call exit_client for it
1242 * --must test this and exit m_nick too!!!
1243 */
1244 register_local_user(source_p, buf);
1245 }
1246 }
1247
1248 /* change_local_nick()
1249 *
1250 * inputs - pointer to server
1251 * - pointer to client
1252 * - nick
1253 * output -
1254 * side effects - changes nick of a LOCAL user
1255 */
1256 void
1257 change_local_nick(struct Client *client_p, struct Client *source_p, const char *nick)
1258 {
1259 int samenick = 0;
1260
1261 /*
1262 * Client just changing his/her nick. If he/she is
1263 * on a channel, send note of change to all clients
1264 * on that channel. Propagate notice to other servers.
1265 */
1266 if ((source_p->localClient->last_nick_change +
1267 General.max_nick_time) < CurrentTime)
1268 source_p->localClient->number_of_nick_changes = 0;
1269 source_p->localClient->last_nick_change = CurrentTime;
1270 source_p->localClient->number_of_nick_changes++;
1271
1272 if ((General.anti_nick_flood &&
1273 (source_p->localClient->number_of_nick_changes
1274 <= General.max_nick_changes)) ||
1275 !General.anti_nick_flood ||
1276 (IsOper(source_p) && General.no_oper_flood))
1277 {
1278 samenick = !irccmp(source_p->name, nick);
1279 if (!samenick)
1280 source_p->tsinfo = CurrentTime;
1281
1282 /* XXX - the format of this notice should eventually be changed
1283 * to either %s[%s@%s], or even better would be get_client_name() -bill
1284 */
1285 sendto_realops_flags(UMODE_NCHANGE, L_ALL, "Nick change: From %s to %s [%s@%s]",
1286 source_p->name, nick, source_p->username, source_p->host);
1287 sendto_common_channels_local(source_p, 1, ":%s!%s@%s NICK :%s",
1288 source_p->name, source_p->username,
1289 source_p->host, nick);
1290
1291 whowas_add_history(source_p, 1);
1292
1293 sendto_server(client_p, source_p, NULL, CAP_TS6, NOCAPS,
1294 ":%s NICK %s :%lu",
1295 ID(source_p), nick, (unsigned long)source_p->tsinfo);
1296 sendto_server(client_p, source_p, NULL, NOCAPS, CAP_TS6,
1297 ":%s NICK %s :%lu",
1298 source_p->name, nick, (unsigned long)source_p->tsinfo);
1299 }
1300 else
1301 {
1302 sendto_one(source_p, form_str(ERR_NICKTOOFAST),
1303 me.name, source_p->name, source_p->name,
1304 nick, General.max_nick_time);
1305 return;
1306 }
1307
1308 /* Finally, add to hash */
1309 assert(source_p->name[0]);
1310
1311 hash_del_client(source_p);
1312
1313 if (!samenick)
1314 {
1315 clear_ban_cache_client(source_p);
1316 watch_check_hash(source_p, RPL_LOGOFF);
1317 }
1318
1319 strcpy(source_p->name, nick);
1320 hash_add_client(source_p);
1321
1322 if (!samenick)
1323 watch_check_hash(source_p, RPL_LOGON);
1324
1325 /* fd_desc is long enough */
1326 fd_note(&client_p->localClient->fd, "Nick: %s", nick);
1327 }
1328
1329 /* log_user_exit()
1330 *
1331 * inputs - pointer to connecting client
1332 * output - NONE
1333 * side effects - Current exiting client is logged to
1334 * either SYSLOG or to file.
1335 */
1336 void
1337 log_user_exit(struct Client *source_p)
1338 {
1339 time_t on_for = CurrentTime - source_p->firsttime;
1340 #ifdef SYSLOG_USERS
1341 if (IsClient(source_p))
1342 {
1343 ilog(L_INFO, "%s (%3ld:%02ld:%02ld): %s!%s@%s %llu/%llu\n",
1344 myctime(source_p->firsttime),
1345 (signed long) on_for / 3600,
1346 (signed long) (on_for % 3600)/60,
1347 (signed long) on_for % 60,
1348 source_p->name, source_p->username, source_p->host,
1349 source_p->localClient->send.bytes>>10,
1350 source_p->localClient->recv.bytes>>10);
1351 }
1352 #else
1353 {
1354 char linebuf[BUFSIZ];
1355
1356 /*
1357 * This conditional makes the logfile active only after
1358 * it's been created - thus logging can be turned off by
1359 * removing the file.
1360 * -Taner
1361 */
1362 if (IsClient(source_p))
1363 {
1364 if (user_log_fb == NULL)
1365 if ((Logging.userlog[0] != '\0') &&
1366 (user_log_fb = fbopen(Logging.userlog, "r")) != NULL)
1367 {
1368 fbclose(user_log_fb);
1369 user_log_fb = fbopen(Logging.userlog, "a");
1370 }
1371
1372 if (user_log_fb != NULL)
1373 {
1374 size_t nbytes = ircsprintf(linebuf,
1375 "%s (%3ld:%02ld:%02ld): %s!%s@%s %llu/%llu\n",
1376 myctime(source_p->firsttime),
1377 (signed long) on_for / 3600,
1378 (signed long) (on_for % 3600)/60,
1379 (signed long) on_for % 60,
1380 source_p->name, source_p->username, source_p->host,
1381 source_p->localClient->send.bytes>>10,
1382 source_p->localClient->recv.bytes>>10);
1383 fbputs(linebuf, user_log_fb, nbytes);
1384 }
1385 }
1386 }
1387 #endif
1388 }
1389
1390
1391 /* log_oper_action()
1392 *
1393 * inputs - type of oper log entry
1394 * - pointer to oper
1395 * - const char *pattern == format string
1396 * - var args for format string
1397 * output - none
1398 * side effects - corresponding log is written to, if its present.
1399 *
1400 * rewritten sept 5 2005 - Dianora
1401 */
1402 void
1403 log_oper_action(int log_type, const struct Client *source_p,
1404 const char *pattern, ...)
1405 {
1406 va_list args;
1407 char linebuf[IRCD_BUFSIZE];
1408 FBFILE *log_fb;
1409 char *logfile;
1410 const char *log_message;
1411 size_t nbytes;
1412 size_t n_preamble;
1413 char *p;
1414
1415 switch(log_type)
1416 {
1417 case LOG_OPER_TYPE:
1418 logfile = Logging.operlog;
1419 log_message = "OPER";
1420 break;
1421 case LOG_FAILED_OPER_TYPE:
1422 logfile = Logging.failed_operlog;
1423 log_message = "FAILED OPER";
1424 break;
1425 case LOG_KLINE_TYPE:
1426 logfile = Logging.klinelog;
1427 log_message = "KLINE";
1428 break;
1429 case LOG_RKLINE_TYPE:
1430 logfile = Logging.klinelog;
1431 log_message = "RKLINE";
1432 break;
1433 case LOG_DLINE_TYPE:
1434 logfile = Logging.klinelog;
1435 log_message = "DLINE";
1436 break;
1437 case LOG_TEMP_DLINE_TYPE:
1438 logfile = Logging.klinelog;
1439 log_message = "TEMP DLINE";
1440 break;
1441 case LOG_TEMP_KLINE_TYPE:
1442 logfile = Logging.klinelog;
1443 log_message = "TEMP KLINE";
1444 break;
1445 case LOG_GLINE_TYPE:
1446 logfile = Logging.glinelog;
1447 log_message = "GLINE";
1448 break;
1449 case LOG_KILL_TYPE:
1450 logfile = Logging.killlog;
1451 log_message = "KILL";
1452 break;
1453 case LOG_IOERR_TYPE:
1454 logfile = Logging.ioerrlog;
1455 log_message = "IO ERR";
1456 break;
1457 default:
1458 return;
1459 }
1460
1461 if (*logfile == '\0')
1462 return;
1463
1464 p = linebuf;
1465 if (source_p != NULL)
1466 {
1467 n_preamble = ircsprintf(linebuf, "%s %s by (%s!%s@%s) :",
1468 myctime(CurrentTime), log_message,
1469 source_p->name, source_p->username, source_p->host);
1470
1471 }
1472 else
1473 {
1474 n_preamble = ircsprintf(linebuf, "%s %s :",
1475 myctime(CurrentTime), log_message);
1476 }
1477
1478 p += n_preamble;
1479
1480 if ((log_fb = fbopen(logfile, "r")) != NULL)
1481 {
1482 fbclose(log_fb);
1483 log_fb = fbopen(logfile, "a");
1484 if (log_fb == NULL)
1485 return;
1486 va_start(args, pattern);
1487 /* XXX add check for IRCD_BUFSIZE-(n_preamble+1) < 0 ? -db */
1488 nbytes = vsnprintf(p, IRCD_BUFSIZE-(n_preamble+1), pattern, args);
1489 nbytes += n_preamble;
1490 va_end(args);
1491 fbputs(linebuf, log_fb, nbytes);
1492 fbclose(log_fb);
1493 }
1494 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28