2 Unix SMB/CIFS implementation.
4 Winbind daemon connection manager
6 Copyright (C) Tim Potter 2001
7 Copyright (C) Andrew Bartlett 2002
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 We need to manage connections to domain controllers without having to
26 mess up the main winbindd code with other issues. The aim of the
27 connection manager is to:
29 - make connections to domain controllers and cache them
30 - re-establish connections when networks or servers go down
31 - centralise the policy on connection timeouts, domain controller
33 - manage re-entrancy for when winbindd becomes able to handle
34 multiple outstanding rpc requests
36 Why not have connection management as part of the rpc layer like tng?
37 Good question. This code may morph into libsmb/rpc_cache.c or something
38 like that but at the moment it's simply staying as part of winbind. I
39 think the TNG architecture of forcing every user of the rpc layer to use
40 the connection caching system is a bad idea. It should be an optional
41 method of using the routines.
43 The TNG design is quite good but I disagree with some aspects of the
51 - I'm pretty annoyed by all the make_nmb_name() stuff. It should be
52 moved down into another function.
54 - There needs to be a utility function in libsmb/namequery.c that does
57 - Take care when destroying cli_structs as they can be shared between
65 #define DBGC_CLASS DBGC_WINBIND
67 /* Global list of connections. Initially a DLIST but can become a hash
68 table or whatever later. */
70 struct winbindd_cm_conn {
71 struct winbindd_cm_conn *prev, *next;
75 struct cli_state *cli;
79 static struct winbindd_cm_conn *cm_conns = NULL;
81 /* Get a domain controller name. Cache positive and negative lookups so we
82 don't go to the network too often when something is badly broken. */
84 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
86 struct get_dc_name_cache {
90 struct get_dc_name_cache *prev, *next;
95 find the DC for a domain using methods appropriate for a ADS domain
97 static BOOL cm_ads_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
100 ads = ads_init(domain, domain, NULL);
105 /* we don't need to bind, just connect */
106 ads->auth.no_bind = 1;
108 DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
111 /* a full ads_connect() is actually overkill, as we don't srictly need
112 to do the SASL auth in order to get the info we need, but libads
113 doesn't offer a better way right now */
117 if (!ads->config.realm) {
121 fstrcpy(srv_name, ads->config.ldap_server_name);
123 *dc_ip = ads->ldap_ip;
126 DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
127 srv_name, inet_ntoa(*dc_ip)));
133 find the DC for a domain using methods appropriate for a RPC domain
135 static BOOL cm_rpc_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
137 struct in_addr *ip_list = NULL;
140 /* Lookup domain controller name. Try the real PDC first to avoid
142 if (!get_dc_list(True, domain, &ip_list, &count)) {
143 if (!get_dc_list(False, domain, &ip_list, &count)) {
144 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
149 /* Pick a nice close server */
150 /* Look for DC on local net */
151 for (i = 0; i < count; i++) {
152 if (!is_local_net(ip_list[i]))
155 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
160 zero_ip(&ip_list[i]);
164 * Secondly try and contact a random PDC/BDC.
167 i = (sys_random() % count);
169 if (!is_zero_ip(ip_list[i]) &&
170 name_status_find(domain, 0x1c, 0x20,
171 ip_list[i], srv_name)) {
176 zero_ip(&ip_list[i]); /* Tried and failed. */
178 /* Finally return first DC that we can contact using a node
180 for (i = 0; i < count; i++) {
181 if (is_zero_ip(ip_list[i]))
184 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
197 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
199 static struct get_dc_name_cache *get_dc_name_cache;
200 struct get_dc_name_cache *dcc;
201 struct in_addr dc_ip;
204 /* Check the cache for previous lookups */
206 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
208 if (!strequal(domain, dcc->domain_name))
209 continue; /* Not our domain */
211 if ((time(NULL) - dcc->lookup_time) >
212 GET_DC_NAME_CACHE_TIMEOUT) {
214 /* Cache entry has expired, delete it */
216 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
218 DLIST_REMOVE(get_dc_name_cache, dcc);
224 /* Return a positive or negative lookup for this domain */
226 if (dcc->srv_name[0]) {
227 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
228 fstrcpy(srv_name, dcc->srv_name);
231 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
236 /* Add cache entry for this lookup. */
238 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
240 if (!(dcc = (struct get_dc_name_cache *)
241 malloc(sizeof(struct get_dc_name_cache))))
246 fstrcpy(dcc->domain_name, domain);
247 dcc->lookup_time = time(NULL);
249 DLIST_ADD(get_dc_name_cache, dcc);
254 if (lp_security() == SEC_ADS) {
255 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
258 /* fall back on rpc methods if the ADS methods fail */
259 ret = cm_rpc_find_dc(domain, &dc_ip, srv_name);
266 /* We have a name so make the cache entry positive now */
267 fstrcpy(dcc->srv_name, srv_name);
269 DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
270 inet_ntoa(dc_ip), domain));
277 /* Choose between anonymous or authenticated connections. We need to use
278 an authenticated connection if DCs have the RestrictAnonymous registry
279 entry set > 0, or the "Additional restrictions for anonymous
280 connections" set in the win2k Local Security Policy.
282 Caller to free() result in domain, username, password
285 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
287 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
288 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
289 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
291 if (*username && **username) {
292 if (!*domain || !**domain) {
293 *domain = smb_xstrdup(lp_workgroup());
296 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
298 DEBUG(3, ("IPC$ connections done anonymously\n"));
299 *username = smb_xstrdup("");
300 *domain = smb_xstrdup("");
301 *password = smb_xstrdup("");
305 /* Open a new smb pipe connection to a DC on a given domain. Cache
306 negative creation attempts so we don't try and connect to broken
307 machines too often. */
309 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
311 struct failed_connection_cache {
316 struct failed_connection_cache *prev, *next;
319 static struct failed_connection_cache *failed_connection_cache;
321 /* Add an entry to the failed conneciton cache */
323 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn,
326 struct failed_connection_cache *fcc;
328 SMB_ASSERT(!NT_STATUS_IS_OK(result));
330 /* Check we already aren't in the cache */
332 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
333 if (strequal(fcc->domain_name, new_conn->domain)) {
334 DEBUG(10, ("domain %s already tried and failed\n",
340 /* Create negative lookup cache entry for this domain and controller */
342 if (!(fcc = (struct failed_connection_cache *)
343 malloc(sizeof(struct failed_connection_cache)))) {
344 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
350 fstrcpy(fcc->domain_name, new_conn->domain);
351 fstrcpy(fcc->controller, new_conn->controller);
352 fcc->lookup_time = time(NULL);
353 fcc->nt_status = result;
355 DLIST_ADD(failed_connection_cache, fcc);
358 /* Open a connction to the remote server, cache failures for 30 seconds */
360 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
361 struct winbindd_cm_conn *new_conn)
363 struct failed_connection_cache *fcc;
364 extern pstring global_myname;
366 char *ipc_username, *ipc_domain, *ipc_password;
367 struct in_addr dc_ip;
371 fstrcpy(new_conn->domain, domain);
372 fstrcpy(new_conn->pipe_name, pipe_name);
374 /* Look for a domain controller for this domain. Negative results
375 are cached so don't bother applying the caching for this
376 function just yet. */
378 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
379 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
380 add_failed_connection_entry(new_conn, result);
384 /* Return false if we have tried to look up this domain and netbios
385 name before and failed. */
387 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
389 if (!(strequal(domain, fcc->domain_name) &&
390 strequal(new_conn->controller, fcc->controller)))
391 continue; /* Not our domain */
393 if ((time(NULL) - fcc->lookup_time) >
394 FAILED_CONNECTION_CACHE_TIMEOUT) {
396 /* Cache entry has expired, delete it */
398 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
400 DLIST_REMOVE(failed_connection_cache, fcc);
406 /* The timeout hasn't expired yet so return false */
408 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
410 result = fcc->nt_status;
411 SMB_ASSERT(!NT_STATUS_IS_OK(result));
415 /* Initialise SMB connection */
417 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
419 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
420 new_conn->controller, global_myname, ipc_domain, ipc_username));
422 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
424 "IPC", ipc_username, ipc_domain,
427 SAFE_FREE(ipc_username);
428 SAFE_FREE(ipc_domain);
429 SAFE_FREE(ipc_password);
431 if (!NT_STATUS_IS_OK(result)) {
432 add_failed_connection_entry(new_conn, result);
436 if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
437 result = NT_STATUS_PIPE_NOT_AVAILABLE;
438 add_failed_connection_entry(new_conn, result);
439 cli_shutdown(new_conn->cli);
446 /* Return true if a connection is still alive */
448 static BOOL connection_ok(struct winbindd_cm_conn *conn)
451 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
456 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
457 conn->controller, conn->domain, conn->pipe_name));
458 smb_panic("connection_ok: conn->cli was null!");
462 if (!conn->cli->initialised) {
463 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
464 conn->controller, conn->domain, conn->pipe_name));
465 smb_panic("connection_ok: conn->cli->initialised is False!");
469 if (conn->cli->fd == -1) {
470 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
471 conn->controller, conn->domain, conn->pipe_name));
478 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
480 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
482 struct winbindd_cm_conn *conn, conn_temp;
485 for (conn = cm_conns; conn; conn = conn->next) {
486 if (strequal(conn->domain, domain) &&
487 strequal(conn->pipe_name, pipe_name)) {
488 if (!connection_ok(conn)) {
490 cli_shutdown(conn->cli);
492 ZERO_STRUCT(conn_temp);
493 conn_temp.next = conn->next;
494 DLIST_REMOVE(cm_conns, conn);
496 conn = &conn_temp; /* Just to keep the loop moving */
504 if (!(conn = malloc(sizeof(*conn))))
505 return NT_STATUS_NO_MEMORY;
509 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
510 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
511 domain, pipe_name, nt_errstr(result)));
515 DLIST_ADD(cm_conns, conn);
522 /* Return a LSA policy handle on a domain */
524 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
526 struct winbindd_cm_conn *conn;
527 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
529 static CLI_POLICY_HND hnd;
531 /* Look for existing connections */
533 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
537 /* This *shitty* code needs scrapping ! JRA */
538 if (policy_handle_is_valid(&conn->pol)) {
544 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
545 des_access, &conn->pol);
547 if (!NT_STATUS_IS_OK(result)) {
548 /* Hit the cache code again. This cleans out the old connection and gets a new one */
549 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
550 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
554 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
555 des_access, &conn->pol);
558 if (!NT_STATUS_IS_OK(result)) {
559 cli_shutdown(conn->cli);
560 DLIST_REMOVE(cm_conns, conn);
572 /* Return a SAM policy handle on a domain */
574 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
576 struct winbindd_cm_conn *conn;
577 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
579 static CLI_POLICY_HND hnd;
581 /* Look for existing connections */
583 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
587 /* This *shitty* code needs scrapping ! JRA */
588 if (policy_handle_is_valid(&conn->pol)) {
593 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
594 des_access, &conn->pol);
596 if (!NT_STATUS_IS_OK(result)) {
597 /* Hit the cache code again. This cleans out the old connection and gets a new one */
598 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
599 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
603 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
604 des_access, &conn->pol);
607 if (!NT_STATUS_IS_OK(result)) {
608 cli_shutdown(conn->cli);
609 DLIST_REMOVE(cm_conns, conn);
621 #if 0 /* This code now *well* out of date */
623 /* Return a SAM domain policy handle on a domain */
625 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
627 struct winbindd_cm_conn *conn, *basic_conn = NULL;
628 static CLI_POLICY_HND hnd;
630 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
632 /* Look for existing connections */
634 for (conn = cm_conns; conn; conn = conn->next) {
635 if (strequal(conn->domain, domain) &&
636 strequal(conn->pipe_name, PIPE_SAMR) &&
637 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
639 if (!connection_ok(conn)) {
640 /* Shutdown cli? Free conn? Allow retry of DC? */
641 DLIST_REMOVE(cm_conns, conn);
649 /* Create a basic handle to open a domain handle from */
651 if (!cm_get_sam_handle(domain))
654 for (conn = cm_conns; conn; conn = conn->next) {
655 if (strequal(conn->domain, domain) &&
656 strequal(conn->pipe_name, PIPE_SAMR) &&
657 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
661 if (!(conn = (struct winbindd_cm_conn *)
662 malloc(sizeof(struct winbindd_cm_conn))))
667 fstrcpy(conn->domain, basic_conn->domain);
668 fstrcpy(conn->controller, basic_conn->controller);
669 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
671 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
672 conn->cli = basic_conn->cli;
674 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
675 &basic_conn->pol, des_access,
676 domain_sid, &conn->pol);
678 if (!NT_STATUS_IS_OK(result))
683 DLIST_ADD(cm_conns, conn);
692 /* Return a SAM policy handle on a domain user */
694 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
697 struct winbindd_cm_conn *conn, *basic_conn = NULL;
698 static CLI_POLICY_HND hnd;
700 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
702 /* Look for existing connections */
704 for (conn = cm_conns; conn; conn = conn->next) {
705 if (strequal(conn->domain, domain) &&
706 strequal(conn->pipe_name, PIPE_SAMR) &&
707 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
708 conn->pipe_data.samr.rid == user_rid) {
710 if (!connection_ok(conn)) {
711 /* Shutdown cli? Free conn? Allow retry of DC? */
712 DLIST_REMOVE(cm_conns, conn);
720 /* Create a domain handle to open a user handle from */
722 if (!cm_get_sam_dom_handle(domain, domain_sid))
725 for (conn = cm_conns; conn; conn = conn->next) {
726 if (strequal(conn->domain, domain) &&
727 strequal(conn->pipe_name, PIPE_SAMR) &&
728 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
733 DEBUG(0, ("No domain sam handle was created!\n"));
737 if (!(conn = (struct winbindd_cm_conn *)
738 malloc(sizeof(struct winbindd_cm_conn))))
743 fstrcpy(conn->domain, basic_conn->domain);
744 fstrcpy(conn->controller, basic_conn->controller);
745 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
747 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
748 conn->cli = basic_conn->cli;
749 conn->pipe_data.samr.rid = user_rid;
751 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
752 &basic_conn->pol, des_access, user_rid,
755 if (!NT_STATUS_IS_OK(result))
760 DLIST_ADD(cm_conns, conn);
769 /* Return a SAM policy handle on a domain group */
771 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
774 struct winbindd_cm_conn *conn, *basic_conn = NULL;
775 static CLI_POLICY_HND hnd;
777 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
779 /* Look for existing connections */
781 for (conn = cm_conns; conn; conn = conn->next) {
782 if (strequal(conn->domain, domain) &&
783 strequal(conn->pipe_name, PIPE_SAMR) &&
784 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
785 conn->pipe_data.samr.rid == group_rid) {
787 if (!connection_ok(conn)) {
788 /* Shutdown cli? Free conn? Allow retry of DC? */
789 DLIST_REMOVE(cm_conns, conn);
797 /* Create a domain handle to open a user handle from */
799 if (!cm_get_sam_dom_handle(domain, domain_sid))
802 for (conn = cm_conns; conn; conn = conn->next) {
803 if (strequal(conn->domain, domain) &&
804 strequal(conn->pipe_name, PIPE_SAMR) &&
805 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
810 DEBUG(0, ("No domain sam handle was created!\n"));
814 if (!(conn = (struct winbindd_cm_conn *)
815 malloc(sizeof(struct winbindd_cm_conn))))
820 fstrcpy(conn->domain, basic_conn->domain);
821 fstrcpy(conn->controller, basic_conn->controller);
822 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
824 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
825 conn->cli = basic_conn->cli;
826 conn->pipe_data.samr.rid = group_rid;
828 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
829 &basic_conn->pol, des_access, group_rid,
832 if (!NT_STATUS_IS_OK(result))
837 DLIST_ADD(cm_conns, conn);
848 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
849 netlogon pipe as no handle is returned. */
851 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
852 struct cli_state **cli)
854 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
855 struct winbindd_cm_conn *conn;
858 return NT_STATUS_INVALID_PARAMETER;
861 /* Open an initial conection */
863 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
867 result = cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
868 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
870 if (!NT_STATUS_IS_OK(result)) {
871 DEBUG(0, ("error connecting to domain password server: %s\n",
874 /* Hit the cache code again. This cleans out the old connection and gets a new one */
875 if (conn->cli->fd == -1) {
876 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
881 result = cli_nt_setup_creds(conn->cli, (lp_server_role() == ROLE_DOMAIN_MEMBER) ?
882 SEC_CHAN_WKSTA : SEC_CHAN_BDC, trust_passwd);
885 if (!NT_STATUS_IS_OK(result)) {
886 cli_shutdown(conn->cli);
887 DLIST_REMOVE(cm_conns, conn);
898 /* Dump the current connection status */
900 static void dump_conn_list(void)
902 struct winbindd_cm_conn *con;
904 DEBUG(0, ("\tDomain Controller Pipe\n"));
906 for(con = cm_conns; con; con = con->next) {
909 /* Display pipe info */
911 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
912 DEBUG(0, ("Error: not enough memory!\n"));
914 DEBUG(0, ("%s\n", msg));
920 void winbindd_cm_status(void)
922 /* List open connections */
924 DEBUG(0, ("winbindd connection manager status:\n"));
929 DEBUG(0, ("\tNo active connections\n"));