ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/src/client.c
Revision: 834
Committed: Sun Jan 7 23:57:47 2007 UTC (18 years, 7 months ago) by bear
Content type: text/x-csrc
File size: 44076 byte(s)
Log Message:
Fix one symptom of what happens when a server's client_p
is unexpectedly exitted during link setup.

This does not fix the underlying bug that we may call exit_client
without being aware of it.

File Contents

# Content
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 "server.h"
38 #include "send.h"
39 #include "whowas.h"
40 #include "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 BlockHeap *lclient_heap = NULL;
59
60 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 attach_class(client_p, 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->idhnext = client_p;
124 client_p->status = STAT_UNKNOWN;
125 strcpy(client_p->username, "unknown");
126
127 return client_p;
128 }
129
130 /*
131 * free_client
132 *
133 * inputs - pointer to client
134 * output - NONE
135 * side effects - client pointed to has its memory freed
136 */
137 static void
138 free_client(struct Client *client_p)
139 {
140 assert(client_p != NULL);
141 assert(client_p != &me);
142 assert(client_p->hnext == client_p);
143 assert(client_p->idhnext == client_p);
144 assert(client_p->channel.head == NULL);
145 assert(dlink_list_length(&client_p->channel) == 0);
146
147 MyFree(client_p->away);
148 MyFree(client_p->serv);
149
150 if (MyConnect(client_p))
151 {
152 assert(dlink_list_length(&client_p->localClient->invited) == 0);
153 assert(client_p->localClient->invited.head == NULL);
154 assert(IsClosing(client_p) && IsDead(client_p));
155
156 MyFree(client_p->localClient->response);
157 MyFree(client_p->localClient->auth_oper);
158
159 /*
160 * clean up extra sockets from P-lines which have been discarded.
161 */
162 if (client_p->localClient->listener)
163 {
164 assert(0 < client_p->localClient->listener->ref_count);
165 if (0 == --client_p->localClient->listener->ref_count &&
166 !client_p->localClient->listener->active)
167 free_listener(client_p->localClient->listener);
168 }
169
170 dbuf_clear(&client_p->localClient->buf_recvq);
171 dbuf_clear(&client_p->localClient->buf_sendq);
172
173 BlockHeapFree(lclient_heap, client_p->localClient);
174 }
175
176 BlockHeapFree(client_heap, client_p);
177 }
178
179 /*
180 * check_pings - go through the local client list and check activity
181 * kill off stuff that should die
182 *
183 * inputs - NOT USED (from event)
184 * output - next time_t when check_pings() should be called again
185 * side effects -
186 *
187 *
188 * A PING can be sent to clients as necessary.
189 *
190 * Client/Server ping outs are handled.
191 */
192
193 /*
194 * Addon from adrian. We used to call this after nextping seconds,
195 * however I've changed it to run once a second. This is only for
196 * PING timeouts, not K/etc-line checks (thanks dianora!). Having it
197 * run once a second makes life a lot easier - when a new client connects
198 * and they need a ping in 4 seconds, if nextping was set to 20 seconds
199 * we end up waiting 20 seconds. This is stupid. :-)
200 * I will optimise (hah!) check_pings() once I've finished working on
201 * tidying up other network IO evilnesses.
202 * -- adrian
203 */
204
205 static void
206 check_pings(void *notused)
207 {
208 check_pings_list(&local_client_list);
209 check_pings_list(&serv_list);
210 check_unknowns_list();
211 }
212
213 /* check_pings_list()
214 *
215 * inputs - pointer to list to check
216 * output - NONE
217 * side effects -
218 */
219 static void
220 check_pings_list(dlink_list *list)
221 {
222 char scratch[32]; // way too generous but...
223 struct Client *client_p; // current local client_p being examined
224 int ping, pingwarn; // ping time value from client
225 dlink_node *ptr, *next_ptr;
226
227 DLINK_FOREACH_SAFE(ptr, next_ptr, list->head)
228 {
229 client_p = ptr->data;
230
231 /*
232 * Note: No need to notify opers here. It's
233 * already done when "FLAGS_DEADSOCKET" is set.
234 */
235 if (IsDead(client_p))
236 {
237 // Ignore it, it's been exited already
238 continue;
239 }
240
241 if (client_p->localClient->reject_delay > 0)
242 {
243 if (client_p->localClient->reject_delay <= CurrentTime)
244 exit_client(client_p, &me, "Rejected");
245 continue;
246 }
247
248 if (GlobalSetOptions.idletime && IsClient(client_p))
249 {
250 if (!IsExemptKline(client_p) && !IsIdlelined(client_p) &&
251 !IsOper(client_p) && ((CurrentTime - client_p->localClient->last) >
252 GlobalSetOptions.idletime))
253 {
254 struct KillConf *conf = MyMalloc(sizeof(struct KillConf));
255
256 conf->access.type = acb_type_kline;
257 DupString(conf->access.user, client_p->username);
258 DupString(conf->access.host, client_p->host);
259 conf->access.expires = CurrentTime + 60;
260 DupString(conf->reason, "idle exceeder");
261 add_access_conf(&conf->access);
262
263 sendto_realops_flags(UMODE_ALL, L_ALL,
264 "Idle time limit exceeded for %s - temp k-lining",
265 get_client_name(client_p, HIDE_IP));
266 exit_client(client_p, &me, conf->reason);
267 continue;
268 }
269 }
270
271 ping = client_p->localClient->class->ping_time;
272 pingwarn = client_p->localClient->class->ping_warning;
273
274 if (ping < CurrentTime - client_p->lasttime)
275 {
276 if (!IsPingSent(client_p))
277 {
278 /*
279 * if we havent PINGed the connection and we havent
280 * heard from it in a while, PING it to make sure
281 * it is still alive.
282 */
283 SetPingSent(client_p);
284 ClearPingWarning(client_p);
285 client_p->lasttime = CurrentTime - ping;
286 sendto_one(client_p, "PING :%s", ID_or_name(&me, client_p));
287 }
288 else
289 {
290 if (CurrentTime - client_p->lasttime >= 2 * ping)
291 {
292 /*
293 * If the client/server hasn't talked to us in 2*ping seconds
294 * and it has a ping time, then close its connection.
295 */
296 if (IsServer(client_p) || IsHandshake(client_p))
297 {
298 sendto_realops_flags(UMODE_ALL, L_ADMIN,
299 "No response from %s, closing link",
300 get_client_name(client_p, HIDE_IP));
301 sendto_realops_flags(UMODE_ALL, L_OPER,
302 "No response from %s, closing link",
303 get_client_name(client_p, MASK_IP));
304 ilog(L_NOTICE, "No response from %s, closing link",
305 get_client_name(client_p, HIDE_IP));
306 }
307 ircsprintf(scratch, "Ping timeout: %d seconds",
308 (int)(CurrentTime - client_p->lasttime));
309
310 exit_client(client_p, &me, scratch);
311 }
312 else if (!IsPingWarning(client_p) && pingwarn > 0 &&
313 (IsServer(client_p) || IsHandshake(client_p)) &&
314 CurrentTime - client_p->lasttime >= ping + pingwarn)
315 {
316 /*
317 * If the server hasn't replied in pingwarn seconds after sending
318 * the PING, notify the opers so that they are aware of the problem.
319 */
320 SetPingWarning(client_p);
321 sendto_realops_flags(UMODE_ALL, L_ADMIN,
322 "Warning, no response from %s in %d seconds",
323 get_client_name(client_p, HIDE_IP), pingwarn);
324 sendto_realops_flags(UMODE_ALL, L_OPER,
325 "Warning, no response from %s in %d seconds",
326 get_client_name(client_p, MASK_IP), pingwarn);
327 ilog(L_NOTICE, "No response from %s in %d seconds",
328 get_client_name(client_p, HIDE_IP), pingwarn);
329 }
330 }
331 }
332 }
333 }
334
335 /* check_unknowns_list()
336 *
337 * inputs - pointer to list of unknown clients
338 * output - NONE
339 * side effects - unknown clients get marked for termination after n seconds
340 */
341 static void
342 check_unknowns_list(void)
343 {
344 dlink_node *ptr = NULL, *next_ptr = NULL;
345
346 DLINK_FOREACH_SAFE(ptr, next_ptr, unknown_list.head)
347 {
348 struct Client *client_p = ptr->data;
349
350 if (client_p->localClient->reject_delay > 0)
351 {
352 if (client_p->localClient->reject_delay <= CurrentTime)
353 exit_client(client_p, &me, "Rejected");
354 continue;
355 }
356
357 /*
358 * Check UNKNOWN connections - if they have been in this state
359 * for > 30s, close them.
360 */
361 if (IsAuthFinished(client_p) && (CurrentTime - client_p->firsttime) > 30)
362 exit_client(client_p, &me, "Registration timed out");
363 }
364 }
365
366 /* check_conf_klines()
367 *
368 * inputs - NONE
369 * output - NONE
370 * side effects - Check all connections for a pending kline against the
371 * client, exit the client if a kline matches.
372 */
373 void
374 check_conf_klines(void)
375 {
376 struct Client *client_p = NULL; // current local client_p being examined
377 dlink_node *ptr, *next_ptr;
378 struct DenyConf *conf;
379 char *type, *reason;
380
381 DLINK_FOREACH_SAFE(ptr, next_ptr, local_client_list.head)
382 {
383 client_p = ptr->data;
384
385 // If a client is already being exited
386 if (IsDead(client_p) || !IsClient(client_p))
387 continue;
388
389 // if there is a returned type then kill it
390 if (execute_callback(is_client_banned, client_p, &type, &reason))
391 {
392 ban_them(client_p, type, reason);
393 continue; // and go examine next fd/client_p
394 }
395 }
396
397 // also check the unknowns list for new dlines
398 DLINK_FOREACH_SAFE(ptr, next_ptr, unknown_list.head)
399 {
400 client_p = ptr->data;
401
402 if ((conf = find_dline(&client_p->localClient->ip)))
403 exit_client(client_p, &me, "D-lined");
404 }
405 }
406
407 /*
408 * ban_them
409 *
410 * inputs - pointer to client to ban
411 * - type of ban (e.g. K-line, X-line)
412 * - ban reason
413 * output - NONE
414 * side effects - given client_p is banned
415 */
416 static void
417 ban_them(struct Client *client_p, const char *type, const char *reason)
418 {
419 const char *user_reason; // What is sent to user
420 const char *channel_reason; // What is sent to channel
421
422 user_reason = (EmptyString(reason) || !General.kline_with_reason) ?
423 type : reason;
424 channel_reason = General.kline_reason ? General.kline_reason : user_reason;
425
426 sendto_realops_flags(UMODE_ALL, L_ALL, "%s active for %s",
427 type, get_client_name(client_p, HIDE_IP));
428
429 if (IsClient(client_p))
430 sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP),
431 me.name, client_p->name, user_reason);
432
433 exit_client(client_p, &me, channel_reason);
434 }
435
436 /* find_person()
437 *
438 * inputs - pointer to name
439 * output - return client pointer
440 * side effects - find person by (nick)name
441 */
442 struct Client *
443 find_person(const struct Client *source_p, const char *name)
444 {
445 struct Client *target_p = NULL;
446
447 if (IsDigit(*name) && IsServer(source_p->from))
448 target_p = hash_find_id(name);
449 else
450 target_p = find_client(name);
451
452 return (target_p && IsClient(target_p)) ? target_p : NULL;
453 }
454
455 /*
456 * find_chasing - find the client structure for a nick name (user)
457 * using history mechanism if necessary. If the client is not found,
458 * an error message (NO SUCH NICK) is generated. If the client was found
459 * through the history, chasing will be 1 and otherwise 0.
460 */
461 struct Client *
462 find_chasing(struct Client *source_p, const char *user, int *chasing)
463 {
464 struct Client *who = find_person(source_p, user);
465
466 if (chasing)
467 *chasing = 0;
468
469 if (who)
470 return who;
471
472 if (IsDigit(*user))
473 return NULL;
474
475 who = whowas_get_history(user, (time_t) General.kill_chase_time_limit);
476 if (!who)
477 {
478 sendto_one(source_p, form_str(ERR_NOSUCHNICK),
479 me.name, source_p->name, user);
480 return NULL;
481 }
482
483 if (chasing)
484 *chasing = 1;
485
486 return who;
487 }
488
489 /*
490 * get_client_name - Return the name of the client
491 * for various tracking and
492 * admin purposes. The main purpose of this function is to
493 * return the "socket host" name of the client, if that
494 * differs from the advertised name (other than case).
495 * But, this can be used to any client structure.
496 *
497 * NOTE 1:
498 * Watch out the allocation of "nbuf", if either source_p->name
499 * or source_p->sockhost gets changed into pointers instead of
500 * directly allocated within the structure...
501 *
502 * NOTE 2:
503 * Function return either a pointer to the structure (source_p) or
504 * to internal buffer (nbuf). *NEVER* use the returned pointer
505 * to modify what it points!!!
506 */
507 const char *
508 get_client_name(const struct Client *client, int showip)
509 {
510 static char nbuf[HOSTLEN * 2 + USERLEN + 5];
511
512 assert(client != NULL);
513
514 if (!irccmp(client->name, client->host))
515 return client->name;
516
517 if (ServerHide.hide_server_ips)
518 if (IsServer(client) || IsConnecting(client) || IsHandshake(client))
519 showip = MASK_IP;
520
521 if (General.hide_spoof_ips)
522 if (showip == SHOW_IP && IsIPSpoof(client))
523 showip = MASK_IP;
524
525 /* And finally, let's get the host information, ip or name */
526 switch (showip)
527 {
528 case SHOW_IP:
529 if (client->sockhost[0] != '\0' || irccmp(client->sockhost, "0") != 0)
530 ircsprintf(nbuf, "%s[%s@%s]", client->name, client->username,
531 client->sockhost);
532 else
533 ircsprintf(nbuf, "%s[%s@%s]", client->name, client->username,
534 client->host);
535 break;
536 case MASK_IP:
537 ircsprintf(nbuf, "%s[%s@255.255.255.255]", client->name,
538 client->username);
539 break;
540 default:
541 ircsprintf(nbuf, "%s[%s@%s]", client->name, client->username,
542 client->host);
543 }
544
545 return nbuf;
546 }
547
548 void
549 free_exited_clients(void)
550 {
551 dlink_node *ptr, *next;
552
553 DLINK_FOREACH_SAFE(ptr, next, dead_list.head)
554 {
555 free_client(ptr->data);
556 dlinkDelete(ptr, &dead_list);
557 free_dlink_node(ptr);
558 }
559 }
560
561 /*
562 * Exit one client, local or remote. Assuming all dependents have
563 * been already removed, and socket closed for local client.
564 *
565 * The only messages generated are QUITs on channels.
566 */
567 static void
568 exit_one_client(struct Client *source_p, const char *quitmsg)
569 {
570 dlink_node *lp = NULL, *next_lp = NULL;
571
572 assert(!IsMe(source_p) && (source_p != &me));
573
574 if (IsClient(source_p))
575 {
576 assert(source_p->servptr);
577 assert(source_p->servptr->serv);
578
579 --Count.total;
580 if (IsOper(source_p))
581 --Count.oper;
582 if (IsInvisible(source_p))
583 --Count.invisi;
584
585 dlinkDelete(&source_p->lnode, &source_p->servptr->serv->client_list);
586
587 /*
588 * If a person is on a channel, send a QUIT notice to every
589 * client (person) on the same channel (so that the client
590 * can show the "**signoff" message). (Note: The notice is
591 * to the local clients *only*)
592 */
593 sendto_common_channels_local(source_p, 0, ":%s!%s@%s QUIT :%s",
594 source_p->name, source_p->username,
595 source_p->host, quitmsg);
596 DLINK_FOREACH_SAFE(lp, next_lp, source_p->channel.head)
597 remove_user_from_channel(lp->data);
598
599 whowas_add_history(source_p, 0);
600 whowas_off_history(source_p);
601
602 assert(source_p->whowas.head == NULL);
603 assert(source_p->whowas.tail == NULL);
604
605 watch_check_hash(source_p, RPL_LOGOFF);
606
607 /* Do local vs. remote processing here */
608 if (MyConnect(source_p))
609 {
610 /* Clean up invitefield */
611 DLINK_FOREACH_SAFE(lp, next_lp, source_p->localClient->invited.head)
612 del_invite(lp->data, source_p);
613
614 /* Clean up allow lists */
615 del_all_accepts(source_p);
616 }
617 }
618 else if (IsServer(source_p))
619 {
620 sendto_realops_flags(UMODE_EXTERNAL, L_ALL,
621 "Server %s split from %s",
622 source_p->name, source_p->servptr->name);
623
624 dlinkDelete(&source_p->lnode, &source_p->servptr->serv->server_list);
625
626 if ((lp = dlinkFindDelete(&global_serv_list, source_p)) != NULL)
627 free_dlink_node(lp);
628 }
629
630 if (splitchecking && !splitmode)
631 check_splitmode(NULL);
632
633 /* Remove source_p from the client lists */
634 if (HasID(source_p))
635 hash_del_id(source_p);
636 if (source_p->name[0])
637 hash_del_client(source_p);
638
639 if (IsUserHostHash(source_p))
640 delete_user_host(source_p->username, source_p->host, !MyConnect(source_p));
641
642 /* remove from global client list
643 * NOTE: source_p->node.next cannot be NULL if the client is added
644 * to global_client_list (there is always &me at its end)
645 */
646 if (source_p != NULL && source_p->node.next != NULL)
647 dlinkDelete(&source_p->node, &global_client_list);
648
649 /* Check to see if the client isn't already on the dead list */
650 assert(dlinkFind(&dead_list, source_p) == NULL);
651
652 /* add to dead client dlist */
653 SetDead(source_p);
654 dlinkAdd(source_p, make_dlink_node(), &dead_list);
655 }
656
657 /* Recursively send QUITs and SQUITs for source_p and all its dependent clients
658 * and servers to those servers that need them. A server needs the client
659 * QUITs if it can't figure them out from the SQUIT (ie pre-TS4) or if it
660 * isn't getting the SQUIT because of @#(*&@)# hostmasking. With TS4, once
661 * a link gets a SQUIT, it doesn't need any QUIT/SQUITs for clients depending
662 * on that one -orabidoo
663 *
664 * This is now called on each local server -adx
665 */
666 static void
667 recurse_send_quits(struct Client *original_source_p, struct Client *source_p,
668 struct Client *from, struct Client *to, const char *comment,
669 const char *splitstr, const char *myname)
670 {
671 dlink_node *ptr, *next;
672 struct Client *target_p;
673 int hidden = match(myname, source_p->name);
674
675 assert(to != source_p); /* should be already removed from serv_list */
676
677 /* If this server can handle quit storm (QS) removal
678 * of dependents, just send the SQUIT
679 *
680 * Always check *all* dependent servers if some of them are
681 * hidden behind fakename. If so, send out the QUITs -adx
682 */
683 if (hidden || !IsCapable(to, CAP_QS))
684 {
685 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->client_list.head)
686 {
687 target_p = ptr->data;
688 sendto_one(to, ":%s QUIT :%s", target_p->name, splitstr);
689 }
690 }
691
692 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->server_list.head)
693 recurse_send_quits(original_source_p, ptr->data, from, to,
694 comment, splitstr, myname);
695
696 if (!hidden && ((source_p == original_source_p && to != from) ||
697 !IsCapable(to, CAP_QS)))
698 {
699 /* don't use a prefix here - we have to be 100% sure the message
700 * will be accepted without Unknown prefix etc.. */
701 sendto_one(to, "SQUIT %s :%s", ID_or_name(source_p, to), comment);
702 }
703 }
704
705 /*
706 * Remove all clients that depend on source_p; assumes all (S)QUITs have
707 * already been sent. we make sure to exit a server's dependent clients
708 * and servers before the server itself; exit_one_client takes care of
709 * actually removing things off llists. tweaked from +CSr31 -orabidoo
710 */
711 static void
712 recurse_remove_clients(struct Client *source_p, const char *quitmsg)
713 {
714 dlink_node *ptr = NULL, *next = NULL;
715
716 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->client_list.head)
717 exit_one_client(ptr->data, quitmsg);
718
719 DLINK_FOREACH_SAFE(ptr, next, source_p->serv->server_list.head)
720 {
721 recurse_remove_clients(ptr->data, quitmsg);
722 exit_one_client(ptr->data, quitmsg);
723 }
724 }
725
726 /*
727 ** Remove *everything* that depends on source_p, from all lists, and sending
728 ** all necessary QUITs and SQUITs. source_p itself is still on the lists,
729 ** and its SQUITs have been sent except for the upstream one -orabidoo
730 */
731 static void
732 remove_dependents(struct Client *source_p, struct Client *from,
733 const char *comment, const char *splitstr)
734 {
735 struct Client *to;
736 struct ConnectConf *conf;
737 static char myname[HOSTLEN+1];
738 dlink_node *ptr;
739
740 DLINK_FOREACH(ptr, serv_list.head)
741 {
742 to = ptr->data;
743
744 if ((conf = to->serv->sconf) != NULL)
745 strlcpy(myname, my_name_for_link(conf), sizeof(myname));
746 else
747 strlcpy(myname, me.name, sizeof(myname));
748 recurse_send_quits(source_p, source_p, from, to,
749 comment, splitstr, myname);
750 }
751
752 recurse_remove_clients(source_p, splitstr);
753 }
754
755 /*
756 * exit_client - exit a client of any type. Generally, you can use
757 * this on any struct Client, regardless of its state.
758 *
759 * Note, you shouldn't exit remote _users_ without first doing
760 * SetKilled and propagating a kill or similar message. However,
761 * it is perfectly correct to call exit_client to force a _server_
762 * quit (either local or remote one).
763 *
764 * inputs: - a client pointer that is going to be exited
765 * - for servers, the second argument is a pointer to who
766 * is firing the server. This side won't get any generated
767 * messages. NEVER NULL!
768 * output: none
769 * side effects: the client is delinked from all lists, disconnected,
770 * and the rest of IRC network is notified of the exit.
771 * Client memory is scheduled to be freed
772 */
773 void
774 exit_client(struct Client *source_p, struct Client *from, const char *comment)
775 {
776 dlink_node *m = NULL;
777
778 if (MyConnect(source_p))
779 {
780 /*
781 * DO NOT REMOVE. exit_client can be called twice after a failed
782 * read/write.
783 */
784 if (IsClosing(source_p))
785 return;
786
787 SetClosing(source_p);
788
789 if (IsIpHash(source_p))
790 remove_one_ip(&source_p->localClient->ip);
791
792 delete_auth(source_p);
793
794 if (IsClient(source_p))
795 {
796 --Count.local;
797
798 if (IsOper(source_p))
799 if ((m = dlinkFindDelete(&oper_list, source_p)) != NULL)
800 free_dlink_node(m);
801
802 dlinkDelete(&source_p->localClient->lclient_node, &local_client_list);
803
804 if (source_p->localClient->list_task != NULL)
805 free_list_task(source_p->localClient->list_task, source_p);
806
807 watch_del_watch_list(source_p);
808 sendto_realops_flags(UMODE_CCONN, L_ALL,
809 "Client exiting: %s (%s@%s) [%s] [%s]",
810 source_p->name, source_p->username, source_p->host, comment,
811 General.hide_spoof_ips && IsIPSpoof(source_p) ?
812 "255.255.255.255" : source_p->sockhost);
813 }
814 else if (IsServer(source_p))
815 {
816 if ((m = dlinkFindDelete(&serv_list, source_p)) != NULL)
817 unset_chcap_usage_counts(source_p);
818
819 --Count.myserver;
820 }
821 else
822 {
823 /*
824 * This source_p could have status of one of STAT_UNKNOWN,
825 * STAT_CONNECTING or STAT_HANDSHAKE
826 * all of which are lumped together into unknown_list
827 *
828 * In all above cases IsRegistered() will not be true.
829 */
830 assert(!IsRegistered(source_p));
831 dlinkDelete(&source_p->localClient->lclient_node, &unknown_list);
832 }
833
834 log_user_exit(source_p);
835
836 if (!IsDead(source_p))
837 {
838 if (IsServer(source_p))
839 {
840 /* for them, we are exiting the network */
841 sendto_one(source_p, ":%s SQUIT %s :%s",
842 ID_or_name(from, source_p), me.name, comment);
843 }
844
845 sendto_one(source_p, "ERROR :Closing Link: %s (%s)",
846 source_p->host, comment);
847 }
848
849 /*
850 * Close the Client connection first and mark it so that no
851 * messages are attempted to send to it. Remember it makes
852 * source_p->from == NULL.
853 */
854 close_connection(source_p);
855 }
856
857 if (IsClient(source_p) && !IsKilled(source_p))
858 {
859 sendto_server(from->from, source_p, NULL, CAP_TS6, NOCAPS,
860 ":%s QUIT :%s", ID(source_p), comment);
861 sendto_server(from->from, source_p, NULL, NOCAPS, CAP_TS6,
862 ":%s QUIT :%s", source_p->name, comment);
863 }
864 else if (IsServer(source_p))
865 {
866 char splitstr[HOSTLEN + HOSTLEN + 2];
867
868 /* This shouldn't ever happen */
869 assert(source_p->serv != NULL && source_p->servptr != NULL);
870
871 if (ServerHide.hide_servers)
872 /*
873 * set netsplit message to "*.net *.split" to still show
874 * that it's a split, but hide the servers splitting
875 */
876 strcpy(splitstr, "*.net *.split");
877 else
878 snprintf(splitstr, sizeof(splitstr), "%s %s",
879 source_p->servptr->name, source_p->name);
880
881 remove_dependents(source_p, from->from, comment, splitstr);
882
883 if (MyConnect(source_p))
884 {
885 sendto_realops_flags(UMODE_ALL, L_ALL,
886 "%s was connected for %d seconds. %llu/%llu sendK/recvK.",
887 source_p->name, (int)(CurrentTime - source_p->firsttime),
888 source_p->localClient->send.bytes >> 10,
889 source_p->localClient->recv.bytes >> 10);
890 ilog(L_NOTICE, "%s was connected for %d seconds. %llu/%llu sendK/recvK.",
891 source_p->name, (int)(CurrentTime - source_p->firsttime),
892 source_p->localClient->send.bytes >> 10,
893 source_p->localClient->recv.bytes >> 10);
894 }
895 }
896
897 /* The client *better* be off all of the lists */
898 assert(dlinkFind(&unknown_list, source_p) == NULL);
899 assert(dlinkFind(&local_client_list, source_p) == NULL);
900 assert(dlinkFind(&serv_list, source_p) == NULL);
901 assert(dlinkFind(&oper_list, source_p) == NULL);
902
903 exit_one_client(source_p, comment);
904 }
905
906 /*
907 * close_connection
908 * Close the physical connection. This function sets client_p->from == NULL.
909 */
910 static void
911 close_connection(struct Client *client_p)
912 {
913 assert(NULL != client_p);
914
915 if (!IsDead(client_p))
916 {
917 /* attempt to flush any pending dbufs. Evil, but .. -- adrian */
918 /* there is still a chance that we might send data to this socket
919 * even if it is marked as blocked (COMM_SELECT_READ handler is called
920 * before COMM_SELECT_WRITE). Let's try, nothing to lose.. -adx
921 */
922 ClearSendqBlocked(client_p);
923 send_queued_write(client_p);
924 }
925
926 if (IsClient(client_p))
927 {
928 ++ServerStats.is_cl;
929 ServerStats.is_cbs += client_p->localClient->send.bytes;
930 ServerStats.is_cbr += client_p->localClient->recv.bytes;
931 ServerStats.is_cti += CurrentTime - client_p->firsttime;
932 }
933 else if (IsServer(client_p))
934 {
935 ++ServerStats.is_sv;
936 ServerStats.is_sbs += client_p->localClient->send.bytes;
937 ServerStats.is_sbr += client_p->localClient->recv.bytes;
938 ServerStats.is_sti += CurrentTime - client_p->firsttime;
939 }
940 else
941 ++ServerStats.is_ni;
942
943 #ifdef HAVE_LIBCRYPTO
944 if (client_p->localClient->fd.ssl)
945 {
946 SSL_set_shutdown(client_p->localClient->fd.ssl, SSL_RECEIVED_SHUTDOWN);
947
948 if (!SSL_shutdown(client_p->localClient->fd.ssl))
949 SSL_shutdown(client_p->localClient->fd.ssl);
950 }
951 #endif
952 if (client_p->localClient->fd.flags.open)
953 fd_close(&client_p->localClient->fd);
954
955 if (HasServlink(client_p))
956 if (client_p->localClient->ctrlfd.flags.open)
957 fd_close(&client_p->localClient->ctrlfd);
958
959 dbuf_clear(&client_p->localClient->buf_sendq);
960 dbuf_clear(&client_p->localClient->buf_recvq);
961
962 MyFree(client_p->localClient->passwd);
963
964 detach_class(client_p);
965 if (client_p->serv && client_p->serv->sconf)
966 {
967 unref_link(client_p->serv->sconf);
968 client_p->serv->sconf = NULL;
969 }
970
971 client_p->from = NULL; /* ...this should catch them! >:) --msa */
972 }
973
974 /*
975 * report_error - report an error from an errno.
976 * Record error to log and also send a copy to all *LOCAL* opers online.
977 *
978 * text is a *format* string for outputing error. It must
979 * contain only two '%s', the first will be replaced
980 * by the sockhost from the client_p, and the latter will
981 * be taken from sys_errlist[errno].
982 *
983 * client_p if not NULL, is the *LOCAL* client associated with
984 * the error.
985 *
986 * Cannot use perror() within daemon. stderr is closed in
987 * ircd and cannot be used. And, worse yet, it might have
988 * been reassigned to a normal connection...
989 *
990 * Actually stderr is still there IFF ircd was run with -s --Rodder
991 */
992 void
993 report_error(int level, const char *text, const char *who, int error)
994 {
995 who = (who != NULL) ? who : "";
996
997 sendto_realops_flags(UMODE_DEBUG, level, text, who, strerror(error));
998 log_oper_action(LOG_IOERR_TYPE, NULL, "%s %s %s\n", who, text, strerror(error));
999 ilog(L_ERROR, text, who, strerror(error));
1000 }
1001
1002 /*
1003 * dead_link_on_write - report a write error if not already dead,
1004 * mark it as dead then exit it
1005 */
1006 void
1007 dead_link_on_write(struct Client *client_p, int ierrno)
1008 {
1009 dlink_node *ptr;
1010
1011 if (IsDefunct(client_p))
1012 return;
1013
1014 dbuf_clear(&client_p->localClient->buf_recvq);
1015 dbuf_clear(&client_p->localClient->buf_sendq);
1016
1017 assert(dlinkFind(&abort_list, client_p) == NULL);
1018 ptr = make_dlink_node();
1019 /* don't let exit_aborted_clients() finish yet */
1020 dlinkAddTail(client_p, ptr, &abort_list);
1021
1022 if (eac_next == NULL)
1023 eac_next = ptr;
1024
1025 SetDead(client_p); /* You are dead my friend */
1026 }
1027
1028 /*
1029 * dead_link_on_read - report a read error if not already dead,
1030 * mark it as dead then exit it
1031 */
1032 void
1033 dead_link_on_read(struct Client *client_p, int error)
1034 {
1035 char errmsg[IRCD_BUFSIZE];
1036 int current_error;
1037
1038 if (IsDefunct(client_p))
1039 return;
1040
1041 dbuf_clear(&client_p->localClient->buf_recvq);
1042 dbuf_clear(&client_p->localClient->buf_sendq);
1043
1044 current_error = get_sockerr(client_p->localClient->fd.fd);
1045
1046 if (IsServer(client_p) || IsHandshake(client_p))
1047 {
1048 int connected = CurrentTime - client_p->firsttime;
1049
1050 if (error == 0)
1051 {
1052 /* Admins get the real IP */
1053 sendto_realops_flags(UMODE_ALL, L_ADMIN,
1054 "Server %s closed the connection",
1055 get_client_name(client_p, SHOW_IP));
1056
1057 /* Opers get a masked IP */
1058 sendto_realops_flags(UMODE_ALL, L_OPER,
1059 "Server %s closed the connection",
1060 get_client_name(client_p, MASK_IP));
1061
1062 ilog(L_NOTICE, "Server %s closed the connection",
1063 get_client_name(client_p, SHOW_IP));
1064 }
1065 else
1066 {
1067 report_error(L_ADMIN, "Lost connection to %s: %d",
1068 get_client_name(client_p, SHOW_IP), current_error);
1069 report_error(L_OPER, "Lost connection to %s: %d",
1070 get_client_name(client_p, MASK_IP), current_error);
1071 }
1072
1073 sendto_realops_flags(UMODE_ALL, L_ALL,
1074 "%s had been connected for %d day%s, %2d:%02d:%02d",
1075 client_p->name, connected/86400,
1076 (connected/86400 == 1) ? "" : "s",
1077 (connected % 86400) / 3600, (connected % 3600) / 60,
1078 connected % 60);
1079 }
1080
1081 if (error == 0)
1082 strlcpy(errmsg, "Remote host closed the connection",
1083 sizeof(errmsg));
1084 else
1085 ircsprintf(errmsg, "Read error: %s",
1086 strerror(current_error));
1087
1088 exit_client(client_p, &me, errmsg);
1089 }
1090
1091 void
1092 exit_aborted_clients(void)
1093 {
1094 dlink_node *ptr;
1095 struct Client *target_p;
1096 const char *notice;
1097
1098 DLINK_FOREACH_SAFE(ptr, eac_next, abort_list.head)
1099 {
1100 target_p = ptr->data;
1101 eac_next = ptr->next;
1102
1103 if (target_p == NULL)
1104 {
1105 sendto_realops_flags(UMODE_ALL, L_ALL,
1106 "Warning: null client on abort_list!");
1107 dlinkDelete(ptr, &abort_list);
1108 free_dlink_node(ptr);
1109 continue;
1110 }
1111
1112 dlinkDelete(ptr, &abort_list);
1113
1114 if (IsSendQExceeded(target_p))
1115 notice = "Max SendQ exceeded";
1116 else
1117 notice = "Write error: connection closed";
1118
1119 exit_client(target_p, &me, notice);
1120 free_dlink_node(ptr);
1121 }
1122 }
1123
1124 /*
1125 * accept processing, this adds a form of "caller ID" to ircd
1126 *
1127 * If a client puts themselves into "caller ID only" mode,
1128 * only clients that match a client pointer they have put on
1129 * the accept list will be allowed to message them.
1130 *
1131 * Diane Bruce, "Dianora" db@db.net
1132 */
1133
1134 void
1135 del_accept(struct Accept *acceptvar, struct Client *client_p)
1136 {
1137 dlinkDelete(&acceptvar->node, &client_p->localClient->acceptlist);
1138
1139 MyFree(acceptvar->nick);
1140 MyFree(acceptvar->user);
1141 MyFree(acceptvar->host);
1142 MyFree(acceptvar);
1143 }
1144
1145 struct Accept *
1146 find_accept(const char *nick, const char *user,
1147 const char *host, struct Client *client_p, int do_match)
1148 {
1149 dlink_node *ptr = NULL;
1150 /* XXX We wouldn't need that if match() would return 0 on match */
1151 int (*cmpfunc)(const char *, const char *) = do_match ? match : irccmp;
1152
1153 DLINK_FOREACH(ptr, client_p->localClient->acceptlist.head)
1154 {
1155 struct Accept *acceptvar = ptr->data;
1156
1157 if (cmpfunc(acceptvar->nick, nick) == do_match &&
1158 cmpfunc(acceptvar->user, user) == do_match &&
1159 cmpfunc(acceptvar->host, host) == do_match)
1160 return acceptvar;
1161 }
1162
1163 return NULL;
1164 }
1165
1166 /* accept_message()
1167 *
1168 * inputs - pointer to source client
1169 * - pointer to target client
1170 * output - 1 if accept this message 0 if not
1171 * side effects - See if source is on target's allow list
1172 */
1173 int
1174 accept_message(struct Client *source,
1175 struct Client *target)
1176 {
1177 dlink_node *ptr = NULL;
1178
1179 if (source == target || find_accept(source->name, source->username,
1180 source->host, target, 1))
1181 return 1;
1182
1183 if (IsSoftCallerId(target))
1184 DLINK_FOREACH(ptr, target->channel.head)
1185 if (IsMember(source, ((struct Membership *)ptr->data)->chptr))
1186 return 1;
1187
1188 return 0;
1189 }
1190
1191 /* del_all_accepts()
1192 *
1193 * inputs - pointer to exiting client
1194 * output - NONE
1195 * side effects - Walk through given clients acceptlist and remove all entries
1196 */
1197 void
1198 del_all_accepts(struct Client *client_p)
1199 {
1200 dlink_node *ptr = NULL, *next_ptr = NULL;
1201
1202 DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->localClient->acceptlist.head)
1203 del_accept(ptr->data, client_p);
1204 }
1205
1206 /* set_initial_nick()
1207 *
1208 * inputs
1209 * output
1210 * side effects -
1211 *
1212 * This function is only called to set up an initially registering
1213 * client.
1214 */
1215 void
1216 set_initial_nick(struct Client *source_p, const char *nick)
1217 {
1218 char buf[USERLEN + 1];
1219
1220 /* This had to be copied here to avoid problems.. */
1221 source_p->tsinfo = CurrentTime;
1222 source_p->localClient->registration &= ~REG_NEED_NICK;
1223
1224 if (source_p->name[0])
1225 hash_del_client(source_p);
1226
1227 strlcpy(source_p->name, nick, sizeof(source_p->name));
1228 hash_add_client(source_p);
1229
1230 /* fd_desc is long enough */
1231 fd_note(&source_p->localClient->fd, "Nick: %s", nick);
1232
1233 if (!source_p->localClient->registration)
1234 {
1235 strlcpy(buf, source_p->username, sizeof(buf));
1236
1237 /*
1238 * USER already received, now we have NICK.
1239 * *NOTE* For servers "NICK" *must* precede the
1240 * user message (giving USER before NICK is possible
1241 * only for local client connection!). note that register_user
1242 * may reject the client and call exit_client for it
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