]> jfr.im git - solanum.git/blame - ircd/client.c
ircd: start staging for relocatable paths
[solanum.git] / ircd / client.c
CommitLineData
212380e3 1/*
b37021a4 2 * charybdis: an advanced ircd.
212380e3
AC
3 * client.c: Controls clients.
4 *
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center
6 * Copyright (C) 1996-2002 Hybrid Development Team
7 * Copyright (C) 2002-2005 ircd-ratbox development team
b37021a4 8 * Copyright (C) 2007 William Pitcock
212380e3
AC
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA
212380e3
AC
24 */
25#include "stdinc.h"
9b8e9eb3 26#include "defaults.h"
212380e3 27
212380e3
AC
28#include "client.h"
29#include "class.h"
212380e3 30#include "hash.h"
4562c604 31#include "match.h"
212380e3 32#include "ircd.h"
212380e3
AC
33#include "numeric.h"
34#include "packet.h"
35#include "s_auth.h"
212380e3
AC
36#include "s_conf.h"
37#include "s_newconf.h"
4016731b 38#include "logger.h"
212380e3
AC
39#include "s_serv.h"
40#include "s_stats.h"
41#include "send.h"
42#include "whowas.h"
43#include "s_user.h"
212380e3 44#include "hash.h"
212380e3 45#include "hostmask.h"
212380e3
AC
46#include "listener.h"
47#include "hook.h"
48#include "msg.h"
49#include "monitor.h"
50#include "blacklist.h"
54015b5f 51#include "reject.h"
994544c2 52#include "scache.h"
a4bf26dd 53#include "rb_dictionary.h"
c6d72037 54#include "sslproc.h"
77d3d2db 55#include "s_assert.h"
212380e3
AC
56
57#define DEBUG_EXITED_CLIENTS
58
330fc5c1
AC
59static void check_pings_list(rb_dlink_list * list);
60static void check_unknowns_list(rb_dlink_list * list);
212380e3
AC
61static void free_exited_clients(void *unused);
62static void exit_aborted_clients(void *unused);
63
64static int exit_remote_client(struct Client *, struct Client *, struct Client *,const char *);
65static int exit_remote_server(struct Client *, struct Client *, struct Client *,const char *);
66static int exit_local_client(struct Client *, struct Client *, struct Client *,const char *);
67static int exit_unknown_client(struct Client *, struct Client *, struct Client *,const char *);
68static int exit_local_server(struct Client *, struct Client *, struct Client *,const char *);
69static int qs_server(struct Client *, struct Client *, struct Client *, const char *comment);
70
71static EVH check_pings;
72
5433b83e
VY
73static rb_bh *client_heap = NULL;
74static rb_bh *lclient_heap = NULL;
b09cbaa3 75static rb_bh *pclient_heap = NULL;
5433b83e 76static rb_bh *user_heap = NULL;
c127b45b 77static rb_bh *away_heap = NULL;
5433b83e 78static char current_uid[IDLEN];
b5b4a0e7 79static int32_t current_connid = 0;
212380e3 80
4177311e 81rb_dictionary *nd_dict = NULL;
43de0f45 82
212380e3
AC
83enum
84{
85 D_LINED,
aae358c0 86 K_LINED
212380e3
AC
87};
88
330fc5c1 89rb_dlink_list dead_list;
212380e3 90#ifdef DEBUG_EXITED_CLIENTS
330fc5c1 91static rb_dlink_list dead_remote_list;
212380e3
AC
92#endif
93
94struct abort_client
95{
330fc5c1 96 rb_dlink_node node;
212380e3
AC
97 struct Client *client;
98 char notice[REASONLEN];
99};
100
330fc5c1 101static rb_dlink_list abort_list;
212380e3
AC
102
103
104/*
105 * init_client
106 *
107 * inputs - NONE
108 * output - NONE
109 * side effects - initialize client free memory
110 */
111void
112init_client(void)
113{
114 /*
115 * start off the check ping event .. -- adrian
116 * Every 30 seconds is plenty -- db
117 */
1087485c 118 client_heap = rb_bh_create(sizeof(struct Client), CLIENT_HEAP_SIZE, "client_heap");
adc6cc42
VY
119 lclient_heap = rb_bh_create(sizeof(struct LocalUser), LCLIENT_HEAP_SIZE, "lclient_heap");
120 pclient_heap = rb_bh_create(sizeof(struct PreClient), PCLIENT_HEAP_SIZE, "pclient_heap");
b09cbaa3 121 user_heap = rb_bh_create(sizeof(struct User), USER_HEAP_SIZE, "user_heap");
c127b45b 122 away_heap = rb_bh_create(AWAYLEN, AWAY_HEAP_SIZE, "away_heap");
adc6cc42 123
3b2ebd04
JT
124 rb_event_addish("check_pings", check_pings, NULL, 30);
125 rb_event_addish("free_exited_clients", &free_exited_clients, NULL, 4);
126 rb_event_addish("exit_aborted_clients", exit_aborted_clients, NULL, 1);
5a606a8f 127 rb_event_add("flood_recalc", flood_recalc, NULL, 1);
43de0f45 128
a4bf26dd 129 nd_dict = rb_dictionary_create("nickdelay", irccmp);
212380e3
AC
130}
131
132
133/*
134 * make_client - create a new Client struct and set it to initial state.
135 *
136 * from == NULL, create local client (a client connected
137 * to a socket).
138 *
139 * from, create remote client (behind a socket
140 * associated with the client defined by
141 * 'from'). ('from' is a local client!!).
142 */
143struct Client *
144make_client(struct Client *from)
145{
146 struct Client *client_p = NULL;
147 struct LocalUser *localClient;
148
398b6a73 149 client_p = rb_bh_alloc(client_heap);
212380e3
AC
150
151 if(from == NULL)
152 {
153 client_p->from = client_p; /* 'from' of local client is self! */
154
b09cbaa3 155 localClient = rb_bh_alloc(lclient_heap);
212380e3
AC
156 SetMyConnect(client_p);
157 client_p->localClient = localClient;
158
e3354945 159 client_p->localClient->lasttime = client_p->localClient->firsttime = rb_current_time();
212380e3 160
c5c2f506 161 client_p->localClient->F = NULL;
212380e3 162
b5b4a0e7
AC
163 if(current_connid+1 == 0)
164 current_connid++;
165
166 client_p->localClient->connid = ++current_connid;
167
168 if(current_connid+1 == 0)
169 current_connid++;
170
171 client_p->localClient->zconnid = ++current_connid;
172 add_to_cli_connid_hash(client_p);
173
5ef68b13 174 client_p->preClient = rb_bh_alloc(pclient_heap);
212380e3
AC
175
176 /* as good a place as any... */
330fc5c1 177 rb_dlinkAdd(client_p, &client_p->localClient->tnode, &unknown_list);
212380e3
AC
178 }
179 else
180 { /* from is not NULL */
181 client_p->localClient = NULL;
182 client_p->preClient = NULL;
183 client_p->from = from; /* 'from' of local client is self! */
184 }
55abcbb2 185
212380e3
AC
186 SetUnknown(client_p);
187 strcpy(client_p->username, "unknown");
188
189 return client_p;
190}
191
192void
193free_pre_client(struct Client *client_p)
194{
195 struct Blacklist *blptr;
196
197 s_assert(NULL != client_p);
198
199 if(client_p->preClient == NULL)
200 return;
201
202 blptr = client_p->preClient->dnsbl_listed;
203 if (blptr != NULL)
204 unref_blacklist(blptr);
7fc09bc4
KB
205 s_assert(rb_dlink_list_length(&client_p->preClient->dnsbl_queries) == 0);
206
398b6a73 207 rb_bh_free(pclient_heap, client_p->preClient);
212380e3
AC
208 client_p->preClient = NULL;
209}
210
211static void
212free_local_client(struct Client *client_p)
213{
214 s_assert(NULL != client_p);
215 s_assert(&me != client_p);
216
217 if(client_p->localClient == NULL)
218 return;
219
220 /*
221 * clean up extra sockets from P-lines which have been discarded.
222 */
223 if(client_p->localClient->listener)
224 {
225 s_assert(0 < client_p->localClient->listener->ref_count);
226 if(0 == --client_p->localClient->listener->ref_count
227 && !client_p->localClient->listener->active)
228 free_listener(client_p->localClient->listener);
229 client_p->localClient->listener = 0;
230 }
231
b5b4a0e7 232 del_from_cli_connid_hash(client_p);
c6d72037
VY
233 if(client_p->localClient->F != NULL)
234 {
3b2ebd04 235 rb_close(client_p->localClient->F);
c6d72037 236 }
212380e3
AC
237
238 if(client_p->localClient->passwd)
239 {
240 memset(client_p->localClient->passwd, 0,
241 strlen(client_p->localClient->passwd));
637c4932 242 rb_free(client_p->localClient->passwd);
212380e3
AC
243 }
244
90afc118 245 rb_free(client_p->localClient->auth_user);
637c4932
VY
246 rb_free(client_p->localClient->challenge);
247 rb_free(client_p->localClient->fullcaps);
248 rb_free(client_p->localClient->opername);
249 rb_free(client_p->localClient->mangledhost);
0e52d0d4
JT
250 if (client_p->localClient->privset)
251 privilegeset_unref(client_p->localClient->privset);
212380e3 252
a4165b42
AS
253 if(IsSSL(client_p))
254 ssld_decrement_clicount(client_p->localClient->ssl_ctl);
55abcbb2 255
a4165b42
AS
256 if(IsCapable(client_p, CAP_ZIP))
257 ssld_decrement_clicount(client_p->localClient->z_ctl);
c6d72037 258
398b6a73 259 rb_bh_free(lclient_heap, client_p->localClient);
212380e3
AC
260 client_p->localClient = NULL;
261}
262
263void
264free_client(struct Client *client_p)
265{
266 s_assert(NULL != client_p);
267 s_assert(&me != client_p);
268 free_local_client(client_p);
269 free_pre_client(client_p);
8eda114a 270 rb_free(client_p->certfp);
398b6a73 271 rb_bh_free(client_heap, client_p);
212380e3
AC
272}
273
274/*
275 * check_pings - go through the local client list and check activity
276 * kill off stuff that should die
277 *
278 * inputs - NOT USED (from event)
279 * output - next time_t when check_pings() should be called again
55abcbb2 280 * side effects -
212380e3
AC
281 *
282 *
283 * A PING can be sent to clients as necessary.
284 *
285 * Client/Server ping outs are handled.
286 */
287
288/*
289 * Addon from adrian. We used to call this after nextping seconds,
290 * however I've changed it to run once a second. This is only for
291 * PING timeouts, not K/etc-line checks (thanks dianora!). Having it
292 * run once a second makes life a lot easier - when a new client connects
293 * and they need a ping in 4 seconds, if nextping was set to 20 seconds
294 * we end up waiting 20 seconds. This is stupid. :-)
295 * I will optimise (hah!) check_pings() once I've finished working on
296 * tidying up other network IO evilnesses.
297 * -- adrian
298 */
299
300static void
301check_pings(void *notused)
302{
303 check_pings_list(&lclient_list);
304 check_pings_list(&serv_list);
305 check_unknowns_list(&unknown_list);
306}
307
308/*
309 * Check_pings_list()
310 *
311 * inputs - pointer to list to check
312 * output - NONE
55abcbb2 313 * side effects -
212380e3
AC
314 */
315static void
330fc5c1 316check_pings_list(rb_dlink_list * list)
212380e3
AC
317{
318 char scratch[32]; /* way too generous but... */
319 struct Client *client_p; /* current local client_p being examined */
320 int ping = 0; /* ping time value from client */
637c4932 321 rb_dlink_node *ptr, *next_ptr;
212380e3 322
637c4932 323 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, list->head)
212380e3
AC
324 {
325 client_p = ptr->data;
326
212380e3
AC
327 if(!MyConnect(client_p) || IsDead(client_p))
328 continue;
329
e2a98043 330 ping = get_client_ping(client_p);
212380e3 331
e3354945 332 if(ping < (rb_current_time() - client_p->localClient->lasttime))
212380e3
AC
333 {
334 /*
335 * If the client/server hasnt talked to us in 2*ping seconds
336 * and it has a ping time, then close its connection.
337 */
e3354945 338 if(((rb_current_time() - client_p->localClient->lasttime) >= (2 * ping)
212380e3
AC
339 && (client_p->flags & FLAGS_PINGSENT)))
340 {
e2a98043 341 if(IsServer(client_p))
212380e3 342 {
e2a98043 343 sendto_realops_snomask(SNO_GENERAL, L_ALL,
212380e3 344 "No response from %s, closing link",
b3ebc7ab 345 client_p->name);
212380e3
AC
346 ilog(L_SERVER,
347 "No response from %s, closing link",
348 log_client_name(client_p, HIDE_IP));
349 }
5203cba5 350 (void) snprintf(scratch, sizeof(scratch),
212380e3 351 "Ping timeout: %d seconds",
e3354945 352 (int) (rb_current_time() - client_p->localClient->lasttime));
212380e3
AC
353
354 exit_client(client_p, client_p, &me, scratch);
355 continue;
356 }
357 else if((client_p->flags & FLAGS_PINGSENT) == 0)
358 {
359 /*
360 * if we havent PINGed the connection and we havent
361 * heard from it in a while, PING it to make sure
362 * it is still alive.
363 */
364 client_p->flags |= FLAGS_PINGSENT;
365 /* not nice but does the job */
e3354945 366 client_p->localClient->lasttime = rb_current_time() - ping;
212380e3
AC
367 sendto_one(client_p, "PING :%s", me.name);
368 }
369 }
370 /* ping_timeout: */
371
372 }
373}
374
375/*
376 * check_unknowns_list
377 *
378 * inputs - pointer to list of unknown clients
379 * output - NONE
380 * side effects - unknown clients get marked for termination after n seconds
381 */
382static void
330fc5c1 383check_unknowns_list(rb_dlink_list * list)
212380e3 384{
637c4932 385 rb_dlink_node *ptr, *next_ptr;
212380e3 386 struct Client *client_p;
35cf4c79 387 int timeout;
212380e3 388
637c4932 389 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, list->head)
212380e3
AC
390 {
391 client_p = ptr->data;
392
393 if(IsDead(client_p) || IsClosing(client_p))
394 continue;
395
6bb4fb83
JT
396 /* still has DNSbls to validate against */
397 if(client_p->preClient != NULL &&
398 rb_dlink_list_length(&client_p->preClient->dnsbl_queries) > 0)
399 continue;
400
212380e3
AC
401 /*
402 * Check UNKNOWN connections - if they have been in this state
403 * for > 30s, close them.
404 */
405
35cf4c79 406 timeout = IsAnyServer(client_p) ? ConfigFileEntry.connect_timeout : 30;
e3354945 407 if((rb_current_time() - client_p->localClient->firsttime) > timeout)
35cf4c79
JT
408 {
409 if(IsAnyServer(client_p))
410 {
411 sendto_realops_snomask(SNO_GENERAL, is_remote_connect(client_p) ? L_NETWIDE : L_ALL,
412 "No response from %s, closing link",
b3ebc7ab 413 client_p->name);
35cf4c79
JT
414 ilog(L_SERVER,
415 "No response from %s, closing link",
416 log_client_name(client_p, HIDE_IP));
417 }
212380e3 418 exit_client(client_p, client_p, &me, "Connection timed out");
35cf4c79 419 }
212380e3
AC
420 }
421}
422
423static void
424notify_banned_client(struct Client *client_p, struct ConfItem *aconf, int ban)
425{
426 static const char conn_closed[] = "Connection closed";
427 static const char d_lined[] = "D-lined";
428 static const char k_lined[] = "K-lined";
212380e3
AC
429 const char *reason = NULL;
430 const char *exit_reason = conn_closed;
431
a12ad044 432 if(ConfigFileEntry.kline_with_reason)
212380e3 433 {
a12ad044 434 reason = get_user_ban_reason(aconf);
b52c2949 435 exit_reason = reason;
212380e3
AC
436 }
437 else
438 {
405ae5ce 439 reason = aconf->status == D_LINED ? d_lined : k_lined;
212380e3
AC
440 }
441
442 if(ban == D_LINED && !IsPerson(client_p))
443 sendto_one(client_p, "NOTICE DLINE :*** You have been D-lined");
444 else
445 sendto_one(client_p, form_str(ERR_YOUREBANNEDCREEP),
446 me.name, client_p->name, reason);
55abcbb2
KB
447
448 exit_client(client_p, client_p, &me,
212380e3
AC
449 EmptyString(ConfigFileEntry.kline_reason) ? exit_reason :
450 ConfigFileEntry.kline_reason);
451}
452
453/*
454 * check_banned_lines
455 * inputs - NONE
456 * output - NONE
170703fe 457 * side effects - Check all connections for a pending k/dline against the
212380e3
AC
458 * client, exit the client if found.
459 */
460void
461check_banned_lines(void)
462{
1bb1f1b0
JT
463 check_dlines();
464 check_klines();
465 check_xlines();
212380e3
AC
466}
467
468/* check_klines_event()
469 *
470 * inputs -
471 * outputs -
472 * side effects - check_klines() is called, kline_queued unset
473 */
474void
475check_klines_event(void *unused)
476{
f66f0baa 477 kline_queued = false;
212380e3
AC
478 check_klines();
479}
480
481/* check_klines
482 *
483 * inputs -
484 * outputs -
485 * side effects - all clients will be checked for klines
486 */
487void
488check_klines(void)
489{
490 struct Client *client_p;
491 struct ConfItem *aconf;
330fc5c1 492 rb_dlink_node *ptr;
637c4932 493 rb_dlink_node *next_ptr;
212380e3 494
637c4932 495 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, lclient_list.head)
212380e3
AC
496 {
497 client_p = ptr->data;
498
499 if(IsMe(client_p) || !IsPerson(client_p))
500 continue;
501
502 if((aconf = find_kline(client_p)) != NULL)
503 {
504 if(IsExemptKline(client_p))
505 {
506 sendto_realops_snomask(SNO_GENERAL, L_ALL,
408024fa
JT
507 "KLINE over-ruled for %s, client is kline_exempt [%s@%s]",
508 get_client_name(client_p, HIDE_IP),
509 aconf->user, aconf->host);
212380e3
AC
510 continue;
511 }
512
513 sendto_realops_snomask(SNO_GENERAL, L_ALL,
514 "KLINE active for %s",
515 get_client_name(client_p, HIDE_IP));
516
517 notify_banned_client(client_p, aconf, K_LINED);
518 continue;
519 }
520 }
521}
522
212380e3
AC
523/* check_dlines()
524 *
525 * inputs -
526 * outputs -
527 * side effects - all clients will be checked for dlines
528 */
529void
530check_dlines(void)
531{
532 struct Client *client_p;
533 struct ConfItem *aconf;
330fc5c1 534 rb_dlink_node *ptr;
637c4932 535 rb_dlink_node *next_ptr;
212380e3 536
637c4932 537 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, lclient_list.head)
212380e3
AC
538 {
539 client_p = ptr->data;
540
541 if(IsMe(client_p))
542 continue;
543
e867208d 544 if((aconf = find_dline((struct sockaddr *)&client_p->localClient->ip, GET_SS_FAMILY(&client_p->localClient->ip))) != NULL)
212380e3
AC
545 {
546 if(aconf->status & CONF_EXEMPTDLINE)
547 continue;
548
549 sendto_realops_snomask(SNO_GENERAL, L_ALL,
550 "DLINE active for %s",
551 get_client_name(client_p, HIDE_IP));
552
553 notify_banned_client(client_p, aconf, D_LINED);
554 continue;
555 }
556 }
557
558 /* dlines need to be checked against unknowns too */
637c4932 559 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, unknown_list.head)
212380e3
AC
560 {
561 client_p = ptr->data;
562
e867208d 563 if((aconf = find_dline((struct sockaddr *)&client_p->localClient->ip, GET_SS_FAMILY(&client_p->localClient->ip))) != NULL)
212380e3
AC
564 {
565 if(aconf->status & CONF_EXEMPTDLINE)
566 continue;
567
568 notify_banned_client(client_p, aconf, D_LINED);
569 }
570 }
571}
572
573/* check_xlines
574 *
575 * inputs -
576 * outputs -
577 * side effects - all clients will be checked for xlines
578 */
579void
580check_xlines(void)
581{
582 struct Client *client_p;
583 struct ConfItem *aconf;
330fc5c1 584 rb_dlink_node *ptr;
637c4932 585 rb_dlink_node *next_ptr;
212380e3 586
637c4932 587 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, lclient_list.head)
212380e3
AC
588 {
589 client_p = ptr->data;
590
591 if(IsMe(client_p) || !IsPerson(client_p))
592 continue;
593
594 if((aconf = find_xline(client_p->info, 1)) != NULL)
595 {
596 if(IsExemptKline(client_p))
597 {
598 sendto_realops_snomask(SNO_GENERAL, L_ALL,
408024fa
JT
599 "XLINE over-ruled for %s, client is kline_exempt [%s]",
600 get_client_name(client_p, HIDE_IP),
70ea02eb 601 aconf->host);
212380e3
AC
602 continue;
603 }
604
605 sendto_realops_snomask(SNO_GENERAL, L_ALL, "XLINE active for %s",
606 get_client_name(client_p, HIDE_IP));
607
608 (void) exit_client(client_p, client_p, &me, "Bad user info");
609 continue;
610 }
611 }
612}
613
330692a1
KB
614/* resv_nick_fnc
615 *
616 * inputs - resv, reason, time
617 * outputs - NONE
618 * side effects - all local clients matching resv will be FNC'd
619 */
620void
621resv_nick_fnc(const char *mask, const char *reason, int temp_time)
622{
623 struct Client *client_p, *target_p;
624 rb_dlink_node *ptr;
625 rb_dlink_node *next_ptr;
626 char *nick;
627 char note[NICKLEN+10];
628
629 if (!ConfigFileEntry.resv_fnc)
630 return;
631
632 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, lclient_list.head)
633 {
634 client_p = ptr->data;
635
636 if(IsMe(client_p) || !IsPerson(client_p) || IsExemptResv(client_p))
637 continue;
638
07d3283f
KB
639 /* Skip users that already have UID nicks. */
640 if(IsDigit(client_p->name[0]))
641 continue;
642
330692a1
KB
643 if(match_esc(mask, client_p->name))
644 {
645 nick = client_p->id;
646
647 /* Tell opers. */
648 sendto_realops_snomask(SNO_GENERAL, L_ALL,
649 "RESV forced nick change for %s!%s@%s to %s; nick matched [%s] (%s)",
650 client_p->name, client_p->username, client_p->host, nick, mask, reason);
651
652 sendto_realops_snomask(SNO_NCHANGE, L_ALL,
653 "Nick change: From %s to %s [%s@%s]",
654 client_p->name, nick, client_p->username, client_p->host);
655
656 /* Tell the user. */
657 if (temp_time > 0)
658 {
659 sendto_one_notice(client_p,
660 ":*** Nick %s is temporarily unavailable on this server.",
661 client_p->name);
662 }
663 else
664 {
665 sendto_one_notice(client_p,
666 ":*** Nick %s is no longer available on this server.",
667 client_p->name);
668 }
669
670 /* Do all of the nick-changing gymnastics. */
671 client_p->tsinfo = rb_current_time();
b47f8a4f 672 whowas_add_history(client_p, 1);
330692a1 673
07d3283f
KB
674 monitor_signoff(client_p);
675
330692a1
KB
676 invalidate_bancache_user(client_p);
677
583f064f 678 sendto_common_channels_local(client_p, NOCAPS, NOCAPS, ":%s!%s@%s NICK :%s",
330692a1
KB
679 client_p->name, client_p->username, client_p->host, nick);
680 sendto_server(client_p, NULL, CAP_TS6, NOCAPS, ":%s NICK %s :%ld",
681 use_id(client_p), nick, (long) client_p->tsinfo);
682
683 del_from_client_hash(client_p->name, client_p);
684 rb_strlcpy(client_p->name, nick, sizeof(client_p->name));
685 add_to_client_hash(nick, client_p);
686
07d3283f
KB
687 monitor_signon(client_p);
688
330692a1
KB
689 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->on_allow_list.head)
690 {
691 target_p = ptr->data;
692 rb_dlinkFindDestroy(client_p, &target_p->localClient->allow_list);
693 rb_dlinkDestroy(ptr, &client_p->on_allow_list);
694 }
695
5203cba5 696 snprintf(note, sizeof(note), "Nick: %s", nick);
330692a1
KB
697 rb_note(client_p->localClient->F, note);
698 }
699 }
700}
701
212380e3
AC
702/*
703 * update_client_exit_stats
704 *
705 * input - pointer to client
706 * output - NONE
55abcbb2 707 * side effects -
212380e3
AC
708 */
709static void
710update_client_exit_stats(struct Client *client_p)
711{
712 if(IsServer(client_p))
713 {
714 sendto_realops_snomask(SNO_EXTERNAL, L_ALL,
715 "Server %s split from %s",
716 client_p->name, client_p->servptr->name);
717 if(HasSentEob(client_p))
718 eob_count--;
719 }
720 else if(IsClient(client_p))
721 {
722 --Count.total;
723 if(IsOper(client_p))
724 --Count.oper;
725 if(IsInvisible(client_p))
726 --Count.invisi;
727 }
728
729 if(splitchecking && !splitmode)
730 check_splitmode(NULL);
731}
732
733/*
734 * release_client_state
735 *
736 * input - pointer to client to release
737 * output - NONE
55abcbb2 738 * side effects -
212380e3
AC
739 */
740static void
741release_client_state(struct Client *client_p)
742{
743 if(client_p->user != NULL)
744 {
745 free_user(client_p->user, client_p); /* try this here */
746 }
747 if(client_p->serv)
748 {
749 if(client_p->serv->user != NULL)
750 free_user(client_p->serv->user, client_p);
751 if(client_p->serv->fullcaps)
637c4932
VY
752 rb_free(client_p->serv->fullcaps);
753 rb_free(client_p->serv);
212380e3
AC
754 }
755}
756
757/*
758 * remove_client_from_list
759 * inputs - point to client to remove
760 * output - NONE
761 * side effects - taken the code from ExitOneClient() for this
762 * and placed it here. - avalon
763 */
764static void
765remove_client_from_list(struct Client *client_p)
766{
767 s_assert(NULL != client_p);
768
769 if(client_p == NULL)
770 return;
771
772 /* A client made with make_client()
773 * is on the unknown_list until removed.
774 * If it =does= happen to exit before its removed from that list
775 * and its =not= on the global_client_list, it will core here.
776 * short circuit that case now -db
777 */
778 if(client_p->node.prev == NULL && client_p->node.next == NULL)
779 return;
780
330fc5c1 781 rb_dlinkDelete(&client_p->node, &global_client_list);
212380e3
AC
782
783 update_client_exit_stats(client_p);
784}
785
786
2d28539c
JT
787/* clean_nick()
788 *
789 * input - nickname to check, flag for nick from local client
790 * output - 0 if erroneous, else 1
791 * side effects -
792 */
793int
794clean_nick(const char *nick, int loc_client)
795{
796 int len = 0;
797
798 /* nicks cant start with a digit or -, and must have a length */
799 if(*nick == '-' || *nick == '\0')
800 return 0;
801
802 if(loc_client && IsDigit(*nick))
803 return 0;
804
805 for(; *nick; nick++)
806 {
807 len++;
808 if(!IsNickChar(*nick))
809 return 0;
810 }
811
812 /* nicklen is +1 */
813 if(len >= NICKLEN && (unsigned int)len >= ConfigFileEntry.nicklen)
814 return 0;
815
816 return 1;
817}
818
212380e3
AC
819/*
820 * find_person - find person by (nick)name.
821 * inputs - pointer to name
822 * output - return client pointer
823 * side effects -
824 */
825struct Client *
826find_person(const char *name)
827{
828 struct Client *c2ptr;
829
830 c2ptr = find_client(name);
831
832 if(c2ptr && IsPerson(c2ptr))
833 return (c2ptr);
834 return (NULL);
835}
836
837struct Client *
838find_named_person(const char *name)
839{
840 struct Client *c2ptr;
841
842 c2ptr = find_named_client(name);
843
844 if(c2ptr && IsPerson(c2ptr))
845 return (c2ptr);
846 return (NULL);
847}
848
849
850/*
55abcbb2
KB
851 * find_chasing - find the client structure for a nick name (user)
852 * using history mechanism if necessary. If the client is not found,
212380e3
AC
853 * an error message (NO SUCH NICK) is generated. If the client was found
854 * through the history, chasing will be 1 and otherwise 0.
855 */
856struct Client *
857find_chasing(struct Client *source_p, const char *user, int *chasing)
858{
859 struct Client *who;
860
861 if(MyClient(source_p))
862 who = find_named_person(user);
863 else
864 who = find_person(user);
865
866 if(chasing)
867 *chasing = 0;
868
869 if(who || IsDigit(*user))
870 return who;
871
b47f8a4f 872 if(!(who = whowas_get_history(user, (long) KILLCHASETIMELIMIT)))
212380e3
AC
873 {
874 sendto_one_numeric(source_p, ERR_NOSUCHNICK,
875 form_str(ERR_NOSUCHNICK), user);
876 return (NULL);
877 }
878 if(chasing)
879 *chasing = 1;
880 return who;
881}
882
883/*
884 * get_client_name - Return the name of the client
885 * for various tracking and
886 * admin purposes. The main purpose of this function is to
887 * return the "socket host" name of the client, if that
888 * differs from the advertised name (other than case).
889 * But, this can be used to any client structure.
890 *
891 * NOTE 1:
892 * Watch out the allocation of "nbuf", if either source_p->name
893 * or source_p->sockhost gets changed into pointers instead of
894 * directly allocated within the structure...
895 *
896 * NOTE 2:
897 * Function return either a pointer to the structure (source_p) or
898 * to internal buffer (nbuf). *NEVER* use the returned pointer
899 * to modify what it points!!!
900 */
901
902const char *
903get_client_name(struct Client *client, int showip)
904{
905 static char nbuf[HOSTLEN * 2 + USERLEN + 5];
906
907 s_assert(NULL != client);
908 if(client == NULL)
909 return NULL;
910
911 if(MyConnect(client))
912 {
913 if(!irccmp(client->name, client->host))
914 return client->name;
915
55abcbb2 916 if(ConfigFileEntry.hide_spoof_ips &&
212380e3
AC
917 showip == SHOW_IP && IsIPSpoof(client))
918 showip = MASK_IP;
212380e3
AC
919 if(IsAnyServer(client))
920 showip = MASK_IP;
212380e3
AC
921
922 /* And finally, let's get the host information, ip or name */
923 switch (showip)
924 {
925 case SHOW_IP:
5203cba5 926 snprintf(nbuf, sizeof(nbuf), "%s[%s@%s]",
55abcbb2 927 client->name, client->username,
212380e3
AC
928 client->sockhost);
929 break;
930 case MASK_IP:
5203cba5 931 snprintf(nbuf, sizeof(nbuf), "%s[%s@255.255.255.255]",
212380e3
AC
932 client->name, client->username);
933 break;
934 default:
5203cba5 935 snprintf(nbuf, sizeof(nbuf), "%s[%s@%s]",
212380e3
AC
936 client->name, client->username, client->host);
937 }
938 return nbuf;
939 }
940
55abcbb2 941 /* As pointed out by Adel Mezibra
212380e3
AC
942 * Neph|l|m@EFnet. Was missing a return here.
943 */
944 return client->name;
945}
55abcbb2 946
212380e3
AC
947/* log_client_name()
948 *
949 * This version is the same as get_client_name, but doesnt contain the
950 * code that will hide IPs always. This should be used for logfiles.
951 */
952const char *
953log_client_name(struct Client *target_p, int showip)
954{
955 static char nbuf[HOSTLEN * 2 + USERLEN + 5];
956
957 if(target_p == NULL)
958 return NULL;
959
960 if(MyConnect(target_p))
961 {
962 if(irccmp(target_p->name, target_p->host) == 0)
963 return target_p->name;
964
965 switch (showip)
966 {
967 case SHOW_IP:
5203cba5 968 snprintf(nbuf, sizeof(nbuf), "%s[%s@%s]", target_p->name,
212380e3
AC
969 target_p->username, target_p->sockhost);
970 break;
971
212380e3 972 default:
5203cba5 973 snprintf(nbuf, sizeof(nbuf), "%s[%s@%s]", target_p->name,
212380e3
AC
974 target_p->username, target_p->host);
975 }
976
977 return nbuf;
978 }
979
980 return target_p->name;
981}
982
983/* is_remote_connect - Returns whether a server was /connect'ed by a remote
984 * oper (send notices netwide) */
985int
986is_remote_connect(struct Client *client_p)
987{
988 struct Client *oper;
989
990 if (client_p->serv == NULL)
991 return FALSE;
992 oper = find_named_person(client_p->serv->by);
993 return oper != NULL && IsOper(oper) && !MyConnect(oper);
994}
995
996static void
997free_exited_clients(void *unused)
998{
330fc5c1 999 rb_dlink_node *ptr, *next;
212380e3
AC
1000 struct Client *target_p;
1001
5cefa1d6 1002 RB_DLINK_FOREACH_SAFE(ptr, next, dead_list.head)
212380e3
AC
1003 {
1004 target_p = ptr->data;
1005
1006#ifdef DEBUG_EXITED_CLIENTS
1007 {
1008 struct abort_client *abt;
330fc5c1 1009 rb_dlink_node *aptr;
212380e3
AC
1010 int found = 0;
1011
5cefa1d6 1012 RB_DLINK_FOREACH(aptr, abort_list.head)
212380e3
AC
1013 {
1014 abt = aptr->data;
1015 if(abt->client == target_p)
1016 {
1017 s_assert(0);
55abcbb2 1018 sendto_realops_snomask(SNO_GENERAL, L_ALL,
212380e3
AC
1019 "On abort_list: %s stat: %u flags: %u/%u handler: %c",
1020 target_p->name, (unsigned int) target_p->status,
1021 target_p->flags, target_p->flags2, target_p->handler);
1022 sendto_realops_snomask(SNO_GENERAL, L_ALL,
f80a1823 1023 "Please report this to the charybdis developers!");
212380e3
AC
1024 found++;
1025 }
1026 }
1027
1028 if(found)
1029 {
330fc5c1 1030 rb_dlinkDestroy(ptr, &dead_list);
212380e3
AC
1031 continue;
1032 }
1033 }
1034#endif
1035
1036 if(ptr->data == NULL)
1037 {
1038 sendto_realops_snomask(SNO_GENERAL, L_ALL,
1039 "Warning: null client on dead_list!");
330fc5c1 1040 rb_dlinkDestroy(ptr, &dead_list);
212380e3
AC
1041 continue;
1042 }
1043 release_client_state(target_p);
1044 free_client(target_p);
330fc5c1 1045 rb_dlinkDestroy(ptr, &dead_list);
212380e3
AC
1046 }
1047
1048#ifdef DEBUG_EXITED_CLIENTS
5cefa1d6 1049 RB_DLINK_FOREACH_SAFE(ptr, next, dead_remote_list.head)
212380e3
AC
1050 {
1051 target_p = ptr->data;
1052
1053 if(ptr->data == NULL)
1054 {
1055 sendto_realops_snomask(SNO_GENERAL, L_ALL,
1056 "Warning: null client on dead_list!");
330fc5c1 1057 rb_dlinkDestroy(ptr, &dead_list);
212380e3
AC
1058 continue;
1059 }
1060 release_client_state(target_p);
1061 free_client(target_p);
330fc5c1 1062 rb_dlinkDestroy(ptr, &dead_remote_list);
212380e3
AC
1063 }
1064#endif
55abcbb2 1065
212380e3
AC
1066}
1067
55abcbb2 1068/*
212380e3 1069** Remove all clients that depend on source_p; assumes all (S)QUITs have
55abcbb2
KB
1070** already been sent. we make sure to exit a server's dependent clients
1071** and servers before the server itself; exit_one_client takes care of
212380e3 1072** actually removing things off llists. tweaked from +CSr31 -orabidoo
cbeab4bc 1073 */
212380e3
AC
1074/*
1075 * added sanity test code.... source_p->serv might be NULL...
1076 */
1077static void
1078recurse_remove_clients(struct Client *source_p, const char *comment)
1079{
1080 struct Client *target_p;
330fc5c1 1081 rb_dlink_node *ptr, *ptr_next;
212380e3
AC
1082
1083 if(IsMe(source_p))
1084 return;
1085
1086 if(source_p->serv == NULL) /* oooops. uh this is actually a major bug */
1087 return;
1088
1089 /* this is very ugly, but it saves cpu :P */
1090 if(ConfigFileEntry.nick_delay > 0)
1091 {
5cefa1d6 1092 RB_DLINK_FOREACH_SAFE(ptr, ptr_next, source_p->serv->users.head)
212380e3
AC
1093 {
1094 target_p = ptr->data;
1095 target_p->flags |= FLAGS_KILLED;
1096 add_nd_entry(target_p->name);
1097
1098 if(!IsDead(target_p) && !IsClosing(target_p))
1099 exit_remote_client(NULL, target_p, &me, comment);
1100 }
1101 }
1102 else
1103 {
5cefa1d6 1104 RB_DLINK_FOREACH_SAFE(ptr, ptr_next, source_p->serv->users.head)
212380e3
AC
1105 {
1106 target_p = ptr->data;
1107 target_p->flags |= FLAGS_KILLED;
1108
1109 if(!IsDead(target_p) && !IsClosing(target_p))
1110 exit_remote_client(NULL, target_p, &me, comment);
1111 }
55abcbb2 1112 }
212380e3 1113
5cefa1d6 1114 RB_DLINK_FOREACH_SAFE(ptr, ptr_next, source_p->serv->servers.head)
212380e3
AC
1115 {
1116 target_p = ptr->data;
1117 recurse_remove_clients(target_p, comment);
1118 qs_server(NULL, target_p, &me, comment);
1119 }
1120}
1121
1122/*
1123** Remove *everything* that depends on source_p, from all lists, and sending
856df9a3 1124** all necessary SQUITs. source_p itself is still on the lists,
212380e3 1125** and its SQUITs have been sent except for the upstream one -orabidoo
cbeab4bc 1126 */
212380e3
AC
1127static void
1128remove_dependents(struct Client *client_p,
1129 struct Client *source_p,
1130 struct Client *from, const char *comment, const char *comment1)
1131{
1132 struct Client *to;
330fc5c1 1133 rb_dlink_node *ptr, *next;
212380e3 1134
5cefa1d6 1135 RB_DLINK_FOREACH_SAFE(ptr, next, serv_list.head)
212380e3
AC
1136 {
1137 to = ptr->data;
1138
856df9a3 1139 if(IsMe(to) || to == source_p->from || to == client_p)
212380e3
AC
1140 continue;
1141
856df9a3 1142 sendto_one(to, "SQUIT %s :%s", get_id(source_p, to), comment);
212380e3
AC
1143 }
1144
1145 recurse_remove_clients(source_p, comment1);
1146}
1147
1148void
1149exit_aborted_clients(void *unused)
1150{
1151 struct abort_client *abt;
330fc5c1 1152 rb_dlink_node *ptr, *next;
5cefa1d6 1153 RB_DLINK_FOREACH_SAFE(ptr, next, abort_list.head)
212380e3
AC
1154 {
1155 abt = ptr->data;
1156
1157#ifdef DEBUG_EXITED_CLIENTS
1158 {
330fc5c1 1159 if(rb_dlinkFind(abt->client, &dead_list))
212380e3
AC
1160 {
1161 s_assert(0);
55abcbb2 1162 sendto_realops_snomask(SNO_GENERAL, L_ALL,
212380e3
AC
1163 "On dead_list: %s stat: %u flags: %u/%u handler: %c",
1164 abt->client->name, (unsigned int) abt->client->status,
1165 abt->client->flags, abt->client->flags2, abt->client->handler);
1166 sendto_realops_snomask(SNO_GENERAL, L_ALL,
f80a1823 1167 "Please report this to the charybdis developers!");
212380e3
AC
1168 continue;
1169 }
1170 }
1171#endif
1172
1173 s_assert(*((unsigned long*)abt->client) != 0xdeadbeef); /* This is lame but its a debug thing */
330fc5c1 1174 rb_dlinkDelete(ptr, &abort_list);
212380e3
AC
1175
1176 if(IsAnyServer(abt->client))
1177 sendto_realops_snomask(SNO_GENERAL, L_ALL,
1178 "Closing link to %s: %s",
b3ebc7ab 1179 abt->client->name, abt->notice);
212380e3
AC
1180
1181 /* its no longer on abort list - we *must* remove
1182 * FLAGS_CLOSING otherwise exit_client() will not run --fl
1183 */
1184 abt->client->flags &= ~FLAGS_CLOSING;
1185 exit_client(abt->client, abt->client, &me, abt->notice);
637c4932 1186 rb_free(abt);
212380e3
AC
1187 }
1188}
1189
1190
1191/*
1192 * dead_link - Adds client to a list of clients that need an exit_client()
212380e3
AC
1193 */
1194void
cef7a7bc 1195dead_link(struct Client *client_p, int sendqex)
212380e3
AC
1196{
1197 struct abort_client *abt;
1198
1199 s_assert(!IsMe(client_p));
1200 if(IsDead(client_p) || IsClosing(client_p) || IsMe(client_p))
1201 return;
1202
eddc2ab6 1203 abt = (struct abort_client *) rb_malloc(sizeof(struct abort_client));
212380e3 1204
cef7a7bc 1205 if(sendqex)
f427c8b0 1206 rb_strlcpy(abt->notice, "Max SendQ exceeded", sizeof(abt->notice));
212380e3 1207 else
5203cba5 1208 snprintf(abt->notice, sizeof(abt->notice), "Write error: %s", strerror(errno));
212380e3
AC
1209
1210 abt->client = client_p;
1211 SetIOError(client_p);
1212 SetDead(client_p);
1213 SetClosing(client_p);
330fc5c1 1214 rb_dlinkAdd(abt, &abt->node, &abort_list);
212380e3
AC
1215}
1216
1217
1218/* This does the remove of the user from channels..local or remote */
1219static inline void
1220exit_generic_client(struct Client *client_p, struct Client *source_p, struct Client *from,
1221 const char *comment)
1222{
637c4932 1223 rb_dlink_node *ptr, *next_ptr;
212380e3
AC
1224
1225 if(IsOper(source_p))
330fc5c1 1226 rb_dlinkFindDestroy(source_p, &oper_list);
212380e3 1227
583f064f 1228 sendto_common_channels_local(source_p, NOCAPS, NOCAPS, ":%s!%s@%s QUIT :%s",
212380e3
AC
1229 source_p->name,
1230 source_p->username, source_p->host, comment);
1231
1232 remove_user_from_channels(source_p);
1233
1234 /* Should not be in any channels now */
1235 s_assert(source_p->user->channel.head == NULL);
1236
1237 /* Clean up invitefield */
637c4932 1238 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, source_p->user->invited.head)
212380e3
AC
1239 {
1240 del_invite(ptr->data, source_p);
1241 }
1242
1243 /* Clean up allow lists */
1244 del_all_accepts(source_p);
1245
b47f8a4f
AC
1246 whowas_add_history(source_p, 0);
1247 whowas_off_history(source_p);
212380e3
AC
1248
1249 monitor_signoff(source_p);
1250
1251 if(has_id(source_p))
1252 del_from_id_hash(source_p->id, source_p);
1253
1254 del_from_hostname_hash(source_p->orighost, source_p);
1255 del_from_client_hash(source_p->name, source_p);
1256 remove_client_from_list(source_p);
1257}
1258
55abcbb2 1259/*
212380e3
AC
1260 * Assumes IsPerson(source_p) && !MyConnect(source_p)
1261 */
1262
1263static int
1264exit_remote_client(struct Client *client_p, struct Client *source_p, struct Client *from,
1265 const char *comment)
1266{
1267 exit_generic_client(client_p, source_p, from, comment);
55abcbb2 1268
212380e3
AC
1269 if(source_p->servptr && source_p->servptr->serv)
1270 {
330fc5c1 1271 rb_dlinkDelete(&source_p->lnode, &source_p->servptr->serv->users);
212380e3
AC
1272 }
1273
1274 if((source_p->flags & FLAGS_KILLED) == 0)
1275 {
1276 sendto_server(client_p, NULL, CAP_TS6, NOCAPS,
1277 ":%s QUIT :%s", use_id(source_p), comment);
212380e3
AC
1278 }
1279
1280 SetDead(source_p);
1281#ifdef DEBUG_EXITED_CLIENTS
330fc5c1 1282 rb_dlinkAddAlloc(source_p, &dead_remote_list);
212380e3 1283#else
330fc5c1 1284 rb_dlinkAddAlloc(source_p, &dead_list);
212380e3
AC
1285#endif
1286 return(CLIENT_EXITED);
1287}
1288
1289/*
07554369 1290 * This assumes IsUnknown(source_p) == true and MyConnect(source_p) == true
212380e3
AC
1291 */
1292
1293static int
1294exit_unknown_client(struct Client *client_p, struct Client *source_p, struct Client *from,
1295 const char *comment)
1296{
1297 delete_auth_queries(source_p);
7fc09bc4 1298 abort_blacklist_queries(source_p);
330fc5c1 1299 rb_dlinkDelete(&source_p->localClient->tnode, &unknown_list);
212380e3
AC
1300
1301 if(!IsIOError(source_p))
dd12a19c
JT
1302 sendto_one(source_p, "ERROR :Closing Link: %s (%s)",
1303 source_p->user != NULL ? source_p->host : "127.0.0.1",
1304 comment);
212380e3
AC
1305
1306 close_connection(source_p);
1307
1308 if(has_id(source_p))
1309 del_from_id_hash(source_p->id, source_p);
1310
1311 del_from_hostname_hash(source_p->host, source_p);
1312 del_from_client_hash(source_p->name, source_p);
1313 remove_client_from_list(source_p);
212380e3 1314 SetDead(source_p);
330fc5c1 1315 rb_dlinkAddAlloc(source_p, &dead_list);
212380e3
AC
1316
1317 /* Note that we don't need to add unknowns to the dead_list */
1318 return(CLIENT_EXITED);
1319}
1320
1321static int
55abcbb2 1322exit_remote_server(struct Client *client_p, struct Client *source_p, struct Client *from,
212380e3
AC
1323 const char *comment)
1324{
1325 static char comment1[(HOSTLEN*2)+2];
1326 static char newcomment[BUFSIZE];
1327 struct Client *target_p;
55abcbb2 1328
212380e3
AC
1329 if(ConfigServerHide.flatten_links)
1330 strcpy(comment1, "*.net *.split");
1331 else
1332 {
66c8fdd2 1333 strcpy(comment1, source_p->servptr->name);
212380e3
AC
1334 strcat(comment1, " ");
1335 strcat(comment1, source_p->name);
1336 }
1337 if (IsPerson(from))
5203cba5 1338 snprintf(newcomment, sizeof(newcomment), "by %s: %s",
212380e3
AC
1339 from->name, comment);
1340
1341 if(source_p->serv != NULL)
1342 remove_dependents(client_p, source_p, from, IsPerson(from) ? newcomment : comment, comment1);
1343
1344 if(source_p->servptr && source_p->servptr->serv)
330fc5c1 1345 rb_dlinkDelete(&source_p->lnode, &source_p->servptr->serv->servers);
212380e3
AC
1346 else
1347 s_assert(0);
1348
330fc5c1 1349 rb_dlinkFindDestroy(source_p, &global_serv_list);
212380e3 1350 target_p = source_p->from;
55abcbb2 1351
212380e3
AC
1352 if(target_p != NULL && IsServer(target_p) && target_p != client_p &&
1353 !IsMe(target_p) && (source_p->flags & FLAGS_KILLED) == 0)
1354 {
55abcbb2 1355 sendto_one(target_p, ":%s SQUIT %s :%s",
212380e3
AC
1356 get_id(from, target_p), get_id(source_p, target_p),
1357 comment);
1358 }
1359
1360 if(has_id(source_p))
1361 del_from_id_hash(source_p->id, source_p);
1362
1363 del_from_client_hash(source_p->name, source_p);
55abcbb2 1364 remove_client_from_list(source_p);
994544c2 1365 scache_split(source_p->serv->nameinfo);
55abcbb2 1366
212380e3
AC
1367 SetDead(source_p);
1368#ifdef DEBUG_EXITED_CLIENTS
330fc5c1 1369 rb_dlinkAddAlloc(source_p, &dead_remote_list);
212380e3 1370#else
330fc5c1 1371 rb_dlinkAddAlloc(source_p, &dead_list);
212380e3
AC
1372#endif
1373 return 0;
1374}
1375
1376static int
55abcbb2 1377qs_server(struct Client *client_p, struct Client *source_p, struct Client *from,
212380e3
AC
1378 const char *comment)
1379{
212380e3 1380 if(source_p->servptr && source_p->servptr->serv)
330fc5c1 1381 rb_dlinkDelete(&source_p->lnode, &source_p->servptr->serv->servers);
212380e3
AC
1382 else
1383 s_assert(0);
1384
330fc5c1 1385 rb_dlinkFindDestroy(source_p, &global_serv_list);
55abcbb2 1386
212380e3
AC
1387 if(has_id(source_p))
1388 del_from_id_hash(source_p->id, source_p);
1389
1390 del_from_client_hash(source_p->name, source_p);
55abcbb2 1391 remove_client_from_list(source_p);
19807b5b 1392 scache_split(source_p->serv->nameinfo);
55abcbb2 1393
212380e3 1394 SetDead(source_p);
55abcbb2 1395 rb_dlinkAddAlloc(source_p, &dead_list);
212380e3
AC
1396 return 0;
1397}
1398
1399static int
55abcbb2 1400exit_local_server(struct Client *client_p, struct Client *source_p, struct Client *from,
212380e3
AC
1401 const char *comment)
1402{
1403 static char comment1[(HOSTLEN*2)+2];
1404 static char newcomment[BUFSIZE];
1405 unsigned int sendk, recvk;
55abcbb2 1406
330fc5c1
AC
1407 rb_dlinkDelete(&source_p->localClient->tnode, &serv_list);
1408 rb_dlinkFindDestroy(source_p, &global_serv_list);
55abcbb2 1409
212380e3
AC
1410 sendk = source_p->localClient->sendK;
1411 recvk = source_p->localClient->receiveK;
1412
1413 /* Always show source here, so the server notices show
1414 * which side initiated the split -- jilles
1415 */
5203cba5 1416 snprintf(newcomment, sizeof(newcomment), "by %s: %s",
212380e3
AC
1417 from == source_p ? me.name : from->name, comment);
1418 if (!IsIOError(source_p))
1419 sendto_one(source_p, "SQUIT %s :%s", use_id(source_p),
1420 newcomment);
1421 if(client_p != NULL && source_p != client_p && !IsIOError(source_p))
1422 {
1423 sendto_one(source_p, "ERROR :Closing Link: 127.0.0.1 %s (%s)",
1424 source_p->name, comment);
1425 }
55abcbb2 1426
212380e3 1427 if(source_p->servptr && source_p->servptr->serv)
330fc5c1 1428 rb_dlinkDelete(&source_p->lnode, &source_p->servptr->serv->servers);
212380e3
AC
1429 else
1430 s_assert(0);
1431
1432
1433 close_connection(source_p);
55abcbb2 1434
212380e3
AC
1435 if(ConfigServerHide.flatten_links)
1436 strcpy(comment1, "*.net *.split");
1437 else
1438 {
66c8fdd2 1439 strcpy(comment1, source_p->servptr->name);
212380e3
AC
1440 strcat(comment1, " ");
1441 strcat(comment1, source_p->name);
1442 }
1443
1444 if(source_p->serv != NULL)
1445 remove_dependents(client_p, source_p, from, IsPerson(from) ? newcomment : comment, comment1);
1446
1447 sendto_realops_snomask(SNO_GENERAL, L_ALL, "%s was connected"
1448 " for %ld seconds. %d/%d sendK/recvK.",
adc6cc42 1449 source_p->name, (long) rb_current_time() - source_p->localClient->firsttime, sendk, recvk);
212380e3
AC
1450
1451 ilog(L_SERVER, "%s was connected for %ld seconds. %d/%d sendK/recvK.",
adc6cc42 1452 source_p->name, (long) rb_current_time() - source_p->localClient->firsttime, sendk, recvk);
55abcbb2 1453
212380e3
AC
1454 if(has_id(source_p))
1455 del_from_id_hash(source_p->id, source_p);
1456
1457 del_from_client_hash(source_p->name, source_p);
1458 remove_client_from_list(source_p);
994544c2 1459 scache_split(source_p->serv->nameinfo);
55abcbb2 1460
212380e3 1461 SetDead(source_p);
330fc5c1 1462 rb_dlinkAddAlloc(source_p, &dead_list);
212380e3
AC
1463 return 0;
1464}
1465
1466
55abcbb2 1467/*
07554369 1468 * This assumes IsPerson(source_p) == true && MyConnect(source_p) == true
212380e3
AC
1469 */
1470
1471static int
1472exit_local_client(struct Client *client_p, struct Client *source_p, struct Client *from,
1473 const char *comment)
1474{
1475 unsigned long on_for;
08d75d97 1476 char tbuf[26];
212380e3
AC
1477
1478 exit_generic_client(client_p, source_p, from, comment);
1479 clear_monitor(source_p);
1480
1481 s_assert(IsPerson(source_p));
330fc5c1
AC
1482 rb_dlinkDelete(&source_p->localClient->tnode, &lclient_list);
1483 rb_dlinkDelete(&source_p->lnode, &me.serv->users);
212380e3
AC
1484
1485 if(IsOper(source_p))
330fc5c1 1486 rb_dlinkFindDestroy(source_p, &local_oper_list);
212380e3
AC
1487
1488 sendto_realops_snomask(SNO_CCONN, L_ALL,
1489 "Client exiting: %s (%s@%s) [%s] [%s]",
1490 source_p->name,
1491 source_p->username, source_p->host, comment,
1492 show_ip(NULL, source_p) ? source_p->sockhost : "255.255.255.255");
1493
1494 sendto_realops_snomask(SNO_CCONNEXT, L_ALL,
1495 "CLIEXIT %s %s %s %s 0 %s",
1496 source_p->name, source_p->username, source_p->host,
1497 show_ip(NULL, source_p) ? source_p->sockhost : "255.255.255.255",
1498 comment);
1499
e3354945 1500 on_for = rb_current_time() - source_p->localClient->firsttime;
212380e3 1501
9641f156 1502 ilog(L_USER, "%s (%3lu:%02lu:%02lu): %s!%s@%s %s %d/%d",
08d75d97 1503 rb_ctime(rb_current_time(), tbuf, sizeof(tbuf)), on_for / 3600,
212380e3
AC
1504 (on_for % 3600) / 60, on_for % 60,
1505 source_p->name, source_p->username, source_p->host,
9641f156 1506 source_p->sockhost,
212380e3
AC
1507 source_p->localClient->sendK, source_p->localClient->receiveK);
1508
1509 sendto_one(source_p, "ERROR :Closing Link: %s (%s)", source_p->host, comment);
1510 close_connection(source_p);
1511
1512 if((source_p->flags & FLAGS_KILLED) == 0)
1513 {
1514 sendto_server(client_p, NULL, CAP_TS6, NOCAPS,
1515 ":%s QUIT :%s", use_id(source_p), comment);
212380e3
AC
1516 }
1517
1518 SetDead(source_p);
330fc5c1 1519 rb_dlinkAddAlloc(source_p, &dead_list);
212380e3
AC
1520 return(CLIENT_EXITED);
1521}
1522
1523
1524/*
1525** exit_client - This is old "m_bye". Name changed, because this is not a
1526** protocol function, but a general server utility function.
1527**
1528** This function exits a client of *any* type (user, server, etc)
1529** from this server. Also, this generates all necessary prototol
1530** messages that this exit may cause.
1531**
1532** 1) If the client is a local client, then this implicitly
1533** exits all other clients depending on this connection (e.g.
1534** remote clients having 'from'-field that points to this.
1535**
1536** 2) If the client is a remote client, then only this is exited.
1537**
1538** For convenience, this function returns a suitable value for
1539** m_function return value:
1540**
1541** CLIENT_EXITED if (client_p == source_p)
1542** 0 if (client_p != source_p)
cbeab4bc 1543 */
212380e3
AC
1544int
1545exit_client(struct Client *client_p, /* The local client originating the
1546 * exit or NULL, if this exit is
1547 * generated by this server for
1548 * internal reasons.
1549 * This will not get any of the
1550 * generated messages. */
1551 struct Client *source_p, /* Client exiting */
1552 struct Client *from, /* Client firing off this Exit,
1553 * never NULL! */
1554 const char *comment /* Reason for the exit */
1555 )
1556{
1557 hook_data_client_exit hdata;
1558 if(IsClosing(source_p))
1559 return -1;
1560
1561 /* note, this HAS to be here, when we exit a client we attempt to
1562 * send them data, if this generates a write error we must *not* add
1563 * them to the abort list --fl
1564 */
1565 SetClosing(source_p);
1566
1567 hdata.local_link = client_p;
1568 hdata.target = source_p;
1569 hdata.from = from;
1570 hdata.comment = comment;
1571 call_hook(h_client_exit, &hdata);
1572
1573 if(MyConnect(source_p))
1574 {
1575 /* Local clients of various types */
1576 if(IsPerson(source_p))
1577 return exit_local_client(client_p, source_p, from, comment);
1578 else if(IsServer(source_p))
1579 return exit_local_server(client_p, source_p, from, comment);
1580 /* IsUnknown || IsConnecting || IsHandShake */
1581 else if(!IsReject(source_p))
1582 return exit_unknown_client(client_p, source_p, from, comment);
55abcbb2
KB
1583 }
1584 else
212380e3
AC
1585 {
1586 /* Remotes */
1587 if(IsPerson(source_p))
1588 return exit_remote_client(client_p, source_p, from, comment);
1589 else if(IsServer(source_p))
1590 return exit_remote_server(client_p, source_p, from, comment);
1591 }
1592
1593 return -1;
1594}
1595
1596/*
1597 * Count up local client memory
1598 */
1599
1600/* XXX one common Client list now */
1601void
1602count_local_client_memory(size_t * count, size_t * local_client_memory_used)
1603{
1087485c
JT
1604 size_t lusage;
1605 rb_bh_usage(lclient_heap, count, NULL, &lusage, NULL);
adc6cc42 1606 *local_client_memory_used = lusage + (*count * (sizeof(void *) + sizeof(struct Client)));
212380e3
AC
1607}
1608
1609/*
1610 * Count up remote client memory
1611 */
1612void
1613count_remote_client_memory(size_t * count, size_t * remote_client_memory_used)
1614{
1087485c
JT
1615 size_t lcount, rcount;
1616 rb_bh_usage(lclient_heap, &lcount, NULL, NULL, NULL);
1617 rb_bh_usage(client_heap, &rcount, NULL, NULL, NULL);
1618 *count = rcount - lcount;
adc6cc42 1619 *remote_client_memory_used = *count * (sizeof(void *) + sizeof(struct Client));
212380e3
AC
1620}
1621
1622
1623/*
1624 * accept processing, this adds a form of "caller ID" to ircd
55abcbb2 1625 *
212380e3 1626 * If a client puts themselves into "caller ID only" mode,
55abcbb2 1627 * only clients that match a client pointer they have put on
212380e3
AC
1628 * the accept list will be allowed to message them.
1629 *
1630 * [ source.on_allow_list ] -> [ target1 ] -> [ target2 ]
1631 *
1632 * [target.allow_list] -> [ source1 ] -> [source2 ]
1633 *
1634 * i.e. a target will have a link list of source pointers it will allow
1635 * each source client then has a back pointer pointing back
1636 * to the client that has it on its accept list.
1637 * This allows for exit_one_client to remove these now bogus entries
55abcbb2 1638 * from any client having an accept on them.
212380e3
AC
1639 */
1640/*
1641 * del_all_accepts
1642 *
1643 * inputs - pointer to exiting client
1644 * output - NONE
1645 * side effects - Walk through given clients allow_list and on_allow_list
1646 * remove all references to this client
1647 */
1648void
1649del_all_accepts(struct Client *client_p)
1650{
330fc5c1 1651 rb_dlink_node *ptr;
637c4932 1652 rb_dlink_node *next_ptr;
212380e3
AC
1653 struct Client *target_p;
1654
1655 if(MyClient(client_p) && client_p->localClient->allow_list.head)
1656 {
1657 /* clear this clients accept list, and remove them from
1658 * everyones on_accept_list
1659 */
637c4932 1660 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->localClient->allow_list.head)
212380e3
AC
1661 {
1662 target_p = ptr->data;
330fc5c1
AC
1663 rb_dlinkFindDestroy(client_p, &target_p->on_allow_list);
1664 rb_dlinkDestroy(ptr, &client_p->localClient->allow_list);
212380e3
AC
1665 }
1666 }
1667
1668 /* remove this client from everyones accept list */
637c4932 1669 RB_DLINK_FOREACH_SAFE(ptr, next_ptr, client_p->on_allow_list.head)
212380e3
AC
1670 {
1671 target_p = ptr->data;
330fc5c1
AC
1672 rb_dlinkFindDestroy(client_p, &target_p->localClient->allow_list);
1673 rb_dlinkDestroy(ptr, &client_p->on_allow_list);
212380e3
AC
1674 }
1675}
1676
1677/*
521f9d63 1678 * show_ip() - asks if the true IP should be shown when source is
55abcbb2 1679 * asking for info about target
212380e3
AC
1680 *
1681 * Inputs - source_p who is asking
1682 * - target_p who do we want the info on
521f9d63 1683 * Output - returns 1 if clear IP can be shown, otherwise 0
212380e3
AC
1684 * Side Effects - none
1685 */
1686
1687int
1688show_ip(struct Client *source_p, struct Client *target_p)
1689{
1690 if(IsAnyServer(target_p))
1691 {
212380e3
AC
1692 return 0;
1693 }
1694 else if(IsIPSpoof(target_p))
1695 {
1696 /* source == NULL indicates message is being sent
1697 * to local opers.
1698 */
1699 if(!ConfigFileEntry.hide_spoof_ips &&
1700 (source_p == NULL || MyOper(source_p)))
1701 return 1;
1702 return 0;
1703 }
1704 else if(IsDynSpoof(target_p) && (source_p != NULL && !IsOper(source_p)))
1705 return 0;
1706 else
1707 return 1;
1708}
1709
1710int
1711show_ip_conf(struct ConfItem *aconf, struct Client *source_p)
1712{
1713 if(IsConfDoSpoofIp(aconf))
1714 {
1715 if(!ConfigFileEntry.hide_spoof_ips && MyOper(source_p))
1716 return 1;
1717
1718 return 0;
1719 }
1720 else
1721 return 1;
1722}
1723
364e59f8
JT
1724int
1725show_ip_whowas(struct Whowas *whowas, struct Client *source_p)
1726{
1727 if(whowas->flags & WHOWAS_IP_SPOOFING)
1728 if(ConfigFileEntry.hide_spoof_ips || !MyOper(source_p))
1729 return 0;
1730 if(whowas->flags & WHOWAS_DYNSPOOF)
1731 if(!IsOper(source_p))
1732 return 0;
1733 return 1;
1734}
1735
212380e3
AC
1736/*
1737 * make_user
1738 *
1739 * inputs - pointer to client struct
1740 * output - pointer to struct User
1741 * side effects - add's an User information block to a client
1742 * if it was not previously allocated.
1743 */
1744struct User *
1745make_user(struct Client *client_p)
1746{
1747 struct User *user;
1748
1749 user = client_p->user;
1750 if(!user)
1751 {
398b6a73 1752 user = (struct User *) rb_bh_alloc(user_heap);
212380e3
AC
1753 user->refcnt = 1;
1754 client_p->user = user;
1755 }
1756 return user;
1757}
1758
1759/*
1760 * make_server
1761 *
1762 * inputs - pointer to client struct
1763 * output - pointer to server_t
1764 * side effects - add's an Server information block to a client
1765 * if it was not previously allocated.
1766 */
8f103562 1767struct Server *
212380e3
AC
1768make_server(struct Client *client_p)
1769{
8f103562 1770 struct Server *serv = client_p->serv;
212380e3
AC
1771
1772 if(!serv)
1773 {
8f103562 1774 serv = (struct Server *) rb_malloc(sizeof(struct Server));
212380e3
AC
1775 client_p->serv = serv;
1776 }
1777 return client_p->serv;
1778}
1779
1780/*
1781 * free_user
55abcbb2 1782 *
212380e3
AC
1783 * inputs - pointer to user struct
1784 * - pointer to client struct
1785 * output - none
1786 * side effects - Decrease user reference count by one and release block,
1787 * if count reaches 0
1788 */
1789void
1790free_user(struct User *user, struct Client *client_p)
1791{
c127b45b
SB
1792 free_away(client_p);
1793
212380e3
AC
1794 if(--user->refcnt <= 0)
1795 {
c127b45b
SB
1796 if(user->away)
1797 rb_free((char *) user->away);
212380e3
AC
1798 /*
1799 * sanity check
1800 */
1801 if(user->refcnt < 0 || user->invited.head || user->channel.head)
1802 {
1803 sendto_realops_snomask(SNO_GENERAL, L_ALL,
0b9aca38
AC
1804 "* %p user (%s!%s@%s) %p %p %p %lu %d *",
1805 client_p,
212380e3
AC
1806 client_p ? client_p->
1807 name : "<noname>",
1808 client_p->username,
1809 client_p->host,
0b9aca38
AC
1810 user,
1811 user->invited.head,
1812 user->channel.head,
330fc5c1 1813 rb_dlink_list_length(&user->channel),
212380e3
AC
1814 user->refcnt);
1815 s_assert(!user->refcnt);
1816 s_assert(!user->invited.head);
1817 s_assert(!user->channel.head);
1818 }
1819
398b6a73 1820 rb_bh_free(user_heap, user);
212380e3
AC
1821 }
1822}
1823
e581ea8e 1824void
c127b45b 1825allocate_away(struct Client *client_p)
e581ea8e 1826{
c127b45b 1827 if(client_p->user->away == NULL)
55abcbb2 1828 client_p->user->away = rb_bh_alloc(away_heap);
e581ea8e
AC
1829}
1830
c127b45b 1831
e581ea8e 1832void
c127b45b 1833free_away(struct Client *client_p)
e581ea8e 1834{
c127b45b
SB
1835 if(client_p->user != NULL && client_p->user->away != NULL) {
1836 rb_bh_free(away_heap, client_p->user->away);
1837 client_p->user->away = NULL;
e581ea8e
AC
1838 }
1839}
1840
212380e3
AC
1841void
1842init_uid(void)
1843{
1844 int i;
1845
1846 for(i = 0; i < 3; i++)
1847 current_uid[i] = me.id[i];
1848
1849 for(i = 3; i < 9; i++)
1850 current_uid[i] = 'A';
1851
1852 current_uid[9] = '\0';
1853}
1854
1855
1856char *
1857generate_uid(void)
1858{
428f9bc1 1859 static int flipped = 0;
212380e3
AC
1860 int i;
1861
428f9bc1 1862uid_restart:
212380e3
AC
1863 for(i = 8; i > 3; i--)
1864 {
1865 if(current_uid[i] == 'Z')
1866 {
1867 current_uid[i] = '0';
428f9bc1 1868 goto out;
212380e3
AC
1869 }
1870 else if(current_uid[i] != '9')
1871 {
1872 current_uid[i]++;
428f9bc1 1873 goto out;
212380e3
AC
1874 }
1875 else
1876 current_uid[i] = 'A';
1877 }
1878
1879 /* if this next if() triggers, we're fucked. */
1880 if(current_uid[3] == 'Z')
1881 {
1882 current_uid[i] = 'A';
428f9bc1 1883 flipped = 1;
212380e3
AC
1884 }
1885 else
1886 current_uid[i]++;
428f9bc1
AC
1887out:
1888 /* if this happens..well, i'm not sure what to say, but lets handle it correctly */
1889 if(rb_unlikely(flipped))
1890 {
1891 /* this slows down uid generation a bit... */
1892 if(find_id(current_uid) != NULL)
1893 goto uid_restart;
1894 }
212380e3
AC
1895 return current_uid;
1896}
1897
1898/*
1899 * close_connection
1900 * Close the physical connection. This function must make
1901 * MyConnect(client_p) == FALSE, and set client_p->from == NULL.
1902 */
1903void
1904close_connection(struct Client *client_p)
1905{
1906 s_assert(client_p != NULL);
1907 if(client_p == NULL)
1908 return;
1909
1910 s_assert(MyConnect(client_p));
1911 if(!MyConnect(client_p))
1912 return;
55abcbb2 1913
212380e3
AC
1914 if(IsServer(client_p))
1915 {
1916 struct server_conf *server_p;
1917
8bd5767b
JT
1918 ServerStats.is_sv++;
1919 ServerStats.is_sbs += client_p->localClient->sendB;
1920 ServerStats.is_sbr += client_p->localClient->receiveB;
679ccbe5 1921 ServerStats.is_sti += (unsigned long long)(rb_current_time() - client_p->localClient->firsttime);
212380e3
AC
1922
1923 /*
1924 * If the connection has been up for a long amount of time, schedule
1925 * a 'quick' reconnect, else reset the next-connect cycle.
1926 */
1927 if((server_p = find_server_conf(client_p->name)) != NULL)
1928 {
1929 /*
1930 * Reschedule a faster reconnect, if this was a automatically
1931 * connected configuration entry. (Note that if we have had
1932 * a rehash in between, the status has been changed to
1933 * CONF_ILLEGAL). But only do this if it was a "good" link.
1934 */
1935 server_p->hold = time(NULL);
1936 server_p->hold +=
1937 (server_p->hold - client_p->localClient->lasttime >
1938 HANGONGOODLINK) ? HANGONRETRYDELAY : ConFreq(server_p->class);
1939 }
1940
1941 }
1942 else if(IsClient(client_p))
1943 {
8bd5767b
JT
1944 ServerStats.is_cl++;
1945 ServerStats.is_cbs += client_p->localClient->sendB;
1946 ServerStats.is_cbr += client_p->localClient->receiveB;
679ccbe5 1947 ServerStats.is_cti += (unsigned long long)(rb_current_time() - client_p->localClient->firsttime);
212380e3
AC
1948 }
1949 else
47adde3d 1950 ServerStats.is_ni++;
54ac8b60 1951
36f0ec74
AC
1952 del_from_cli_connid_hash(client_p);
1953
c6d72037 1954 if(client_p->localClient->F != NULL)
54ac8b60
VY
1955 {
1956 /* attempt to flush any pending dbufs. Evil, but .. -- adrian */
1957 if(!IsIOError(client_p))
1958 send_queued(client_p);
1959
1960 rb_close(client_p->localClient->F);
1961 client_p->localClient->F = NULL;
1962 }
1963
734d420e
JT
1964 rb_linebuf_donebuf(&client_p->localClient->buf_sendq);
1965 rb_linebuf_donebuf(&client_p->localClient->buf_recvq);
212380e3
AC
1966 detach_conf(client_p);
1967
1968 /* XXX shouldnt really be done here. */
1969 detach_server_conf(client_p);
1970
1971 client_p->from = NULL; /* ...this should catch them! >:) --msa */
1972 ClearMyConnect(client_p);
1973 SetIOError(client_p);
1974}
1975
1976
1977
1978void
1979error_exit_client(struct Client *client_p, int error)
1980{
1981 /*
1982 * ...hmm, with non-blocking sockets we might get
1983 * here from quite valid reasons, although.. why
1984 * would select report "data available" when there
1985 * wasn't... so, this must be an error anyway... --msa
1986 * actually, EOF occurs when read() returns 0 and
1987 * in due course, select() returns that fd as ready
1988 * for reading even though it ends up being an EOF. -avalon
1989 */
1990 char errmsg[255];
734d420e 1991 int current_error = rb_get_sockerr(client_p->localClient->F);
212380e3
AC
1992
1993 SetIOError(client_p);
1994
1995 if(IsServer(client_p) || IsHandshake(client_p))
1996 {
212380e3
AC
1997 if(error == 0)
1998 {
1999 sendto_realops_snomask(SNO_GENERAL, is_remote_connect(client_p) && !IsServer(client_p) ? L_NETWIDE : L_ALL,
2000 "Server %s closed the connection",
b3ebc7ab 2001 client_p->name);
212380e3
AC
2002
2003 ilog(L_SERVER, "Server %s closed the connection",
2004 log_client_name(client_p, SHOW_IP));
2005 }
2006 else
2007 {
2008 sendto_realops_snomask(SNO_GENERAL, is_remote_connect(client_p) && !IsServer(client_p) ? L_NETWIDE : L_ALL,
2009 "Lost connection to %s: %s",
2010 client_p->name, strerror(current_error));
2011 ilog(L_SERVER, "Lost connection to %s: %s",
2012 log_client_name(client_p, SHOW_IP), strerror(current_error));
2013 }
212380e3
AC
2014 }
2015
2016 if(error == 0)
f427c8b0 2017 rb_strlcpy(errmsg, "Remote host closed the connection", sizeof(errmsg));
212380e3 2018 else
5203cba5 2019 snprintf(errmsg, sizeof(errmsg), "Read error: %s", strerror(current_error));
212380e3
AC
2020
2021 exit_client(client_p, client_p, &me, errmsg);
2022}