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 const char *realm = domain;
102 if (strcasecmp(realm, lp_workgroup()) == 0) {
106 ads = ads_init(realm, domain, NULL);
111 /* we don't need to bind, just connect */
112 ads->auth.no_bind = 1;
114 DEBUG(4,("cm_ads_find_dc: domain=%s\n", domain));
117 /* a full ads_connect() is actually overkill, as we don't srictly need
118 to do the SASL auth in order to get the info we need, but libads
119 doesn't offer a better way right now */
123 if (!ads->config.realm) {
127 fstrcpy(srv_name, ads->config.ldap_server_name);
129 *dc_ip = ads->ldap_ip;
132 DEBUG(4,("cm_ads_find_dc: using server='%s' IP=%s\n",
133 srv_name, inet_ntoa(*dc_ip)));
139 find the DC for a domain using methods appropriate for a RPC domain
141 static BOOL cm_rpc_find_dc(const char *domain, struct in_addr *dc_ip, fstring srv_name)
143 struct in_addr *ip_list = NULL;
146 /* Lookup domain controller name. Try the real PDC first to avoid
148 if (get_dc_list(True, domain, &ip_list, &count) &&
149 name_status_find(domain, 0x1c, 0x20, ip_list[0], srv_name)) {
155 if (!get_dc_list(False, domain, &ip_list, &count)) {
156 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
160 /* Pick a nice close server */
161 /* Look for DC on local net */
162 for (i = 0; i < count; i++) {
163 if (!is_local_net(ip_list[i]))
166 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
171 zero_ip(&ip_list[i]);
175 * Secondly try and contact a random PDC/BDC.
178 i = (sys_random() % count);
180 if (!is_zero_ip(ip_list[i]) &&
181 name_status_find(domain, 0x1c, 0x20,
182 ip_list[i], srv_name)) {
187 zero_ip(&ip_list[i]); /* Tried and failed. */
189 /* Finally return first DC that we can contact using a node
191 for (i = 0; i < count; i++) {
192 if (is_zero_ip(ip_list[i]))
195 if (name_status_find(domain, 0x1c, 0x20, ip_list[i], srv_name)) {
208 static BOOL cm_get_dc_name(const char *domain, fstring srv_name, struct in_addr *ip_out)
210 static struct get_dc_name_cache *get_dc_name_cache;
211 struct get_dc_name_cache *dcc;
212 struct in_addr dc_ip;
215 /* Check the cache for previous lookups */
217 for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
219 if (!strequal(domain, dcc->domain_name))
220 continue; /* Not our domain */
222 if ((time(NULL) - dcc->lookup_time) >
223 GET_DC_NAME_CACHE_TIMEOUT) {
225 /* Cache entry has expired, delete it */
227 DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
229 DLIST_REMOVE(get_dc_name_cache, dcc);
235 /* Return a positive or negative lookup for this domain */
237 if (dcc->srv_name[0]) {
238 DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
239 fstrcpy(srv_name, dcc->srv_name);
242 DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
247 /* Add cache entry for this lookup. */
249 DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
251 if (!(dcc = (struct get_dc_name_cache *)
252 malloc(sizeof(struct get_dc_name_cache))))
257 fstrcpy(dcc->domain_name, domain);
258 dcc->lookup_time = time(NULL);
260 DLIST_ADD(get_dc_name_cache, dcc);
265 if (lp_security() == SEC_ADS) {
266 ret = cm_ads_find_dc(domain, &dc_ip, srv_name);
269 /* fall back on rpc methods if the ADS methods fail */
270 ret = cm_rpc_find_dc(domain, &dc_ip, srv_name);
277 /* We have a name so make the cache entry positive now */
278 fstrcpy(dcc->srv_name, srv_name);
280 DEBUG(3, ("cm_get_dc_name: Returning DC %s (%s) for domain %s\n", srv_name,
281 inet_ntoa(dc_ip), domain));
288 /* Choose between anonymous or authenticated connections. We need to use
289 an authenticated connection if DCs have the RestrictAnonymous registry
290 entry set > 0, or the "Additional restrictions for anonymous
291 connections" set in the win2k Local Security Policy.
293 Caller to free() result in domain, username, password
296 static void cm_get_ipc_userpass(char **username, char **domain, char **password)
298 *username = secrets_fetch(SECRETS_AUTH_USER, NULL);
299 *domain = secrets_fetch(SECRETS_AUTH_DOMAIN, NULL);
300 *password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
302 if (*username && **username) {
303 if (!*domain || !**domain) {
304 *domain = smb_xstrdup(lp_workgroup());
307 DEBUG(3, ("IPC$ connections done by user %s\\%s\n", *domain, *username));
309 DEBUG(3, ("IPC$ connections done anonymously\n"));
310 *username = smb_xstrdup("");
311 *domain = smb_xstrdup("");
312 *password = smb_xstrdup("");
316 /* Open a new smb pipe connection to a DC on a given domain. Cache
317 negative creation attempts so we don't try and connect to broken
318 machines too often. */
320 #define FAILED_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
322 struct failed_connection_cache {
327 struct failed_connection_cache *prev, *next;
330 static struct failed_connection_cache *failed_connection_cache;
332 /* Add an entry to the failed conneciton cache */
334 static void add_failed_connection_entry(struct winbindd_cm_conn *new_conn,
337 struct failed_connection_cache *fcc;
339 SMB_ASSERT(!NT_STATUS_IS_OK(result));
341 /* Check we already aren't in the cache */
343 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
344 if (strequal(fcc->domain_name, new_conn->domain)) {
345 DEBUG(10, ("domain %s already tried and failed\n",
351 /* Create negative lookup cache entry for this domain and controller */
353 if (!(fcc = (struct failed_connection_cache *)
354 malloc(sizeof(struct failed_connection_cache)))) {
355 DEBUG(0, ("malloc failed in add_failed_connection_entry!\n"));
361 fstrcpy(fcc->domain_name, new_conn->domain);
362 fstrcpy(fcc->controller, new_conn->controller);
363 fcc->lookup_time = time(NULL);
364 fcc->nt_status = result;
366 DLIST_ADD(failed_connection_cache, fcc);
369 /* Open a connction to the remote server, cache failures for 30 seconds */
371 static NTSTATUS cm_open_connection(const char *domain,const char *pipe_name,
372 struct winbindd_cm_conn *new_conn)
374 struct failed_connection_cache *fcc;
375 extern pstring global_myname;
377 char *ipc_username, *ipc_domain, *ipc_password;
378 struct in_addr dc_ip;
382 fstrcpy(new_conn->domain, domain);
383 fstrcpy(new_conn->pipe_name, pipe_name);
385 /* Look for a domain controller for this domain. Negative results
386 are cached so don't bother applying the caching for this
387 function just yet. */
389 if (!cm_get_dc_name(domain, new_conn->controller, &dc_ip)) {
390 result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
391 add_failed_connection_entry(new_conn, result);
395 /* Return false if we have tried to look up this domain and netbios
396 name before and failed. */
398 for (fcc = failed_connection_cache; fcc; fcc = fcc->next) {
400 if (!(strequal(domain, fcc->domain_name) &&
401 strequal(new_conn->controller, fcc->controller)))
402 continue; /* Not our domain */
404 if ((time(NULL) - fcc->lookup_time) >
405 FAILED_CONNECTION_CACHE_TIMEOUT) {
407 /* Cache entry has expired, delete it */
409 DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
411 DLIST_REMOVE(failed_connection_cache, fcc);
417 /* The timeout hasn't expired yet so return false */
419 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
421 result = fcc->nt_status;
422 SMB_ASSERT(!NT_STATUS_IS_OK(result));
426 /* Initialise SMB connection */
428 cm_get_ipc_userpass(&ipc_username, &ipc_domain, &ipc_password);
430 DEBUG(5, ("connecting to %s from %s with username [%s]\\[%s]\n",
431 new_conn->controller, global_myname, ipc_domain, ipc_username));
433 result = cli_full_connection(&(new_conn->cli), global_myname, new_conn->controller,
435 "IPC", ipc_username, ipc_domain,
438 SAFE_FREE(ipc_username);
439 SAFE_FREE(ipc_domain);
440 SAFE_FREE(ipc_password);
442 if (!NT_STATUS_IS_OK(result)) {
443 add_failed_connection_entry(new_conn, result);
447 if (!cli_nt_session_open (new_conn->cli, pipe_name)) {
448 result = NT_STATUS_PIPE_NOT_AVAILABLE;
449 add_failed_connection_entry(new_conn, result);
450 cli_shutdown(new_conn->cli);
457 /* Return true if a connection is still alive */
459 static BOOL connection_ok(struct winbindd_cm_conn *conn)
462 smb_panic("Invalid paramater passed to conneciton_ok(): conn was NULL!\n");
467 DEBUG(0, ("Connection to %s for domain %s (pipe %s) has NULL conn->cli!\n",
468 conn->controller, conn->domain, conn->pipe_name));
469 smb_panic("connection_ok: conn->cli was null!");
473 if (!conn->cli->initialised) {
474 DEBUG(0, ("Connection to %s for domain %s (pipe %s) was never initialised!\n",
475 conn->controller, conn->domain, conn->pipe_name));
476 smb_panic("connection_ok: conn->cli->initialised is False!");
480 if (conn->cli->fd == -1) {
481 DEBUG(3, ("Connection to %s for domain %s (pipe %s) has died or was never started (fd == -1)\n",
482 conn->controller, conn->domain, conn->pipe_name));
489 /* Get a connection to the remote DC and open the pipe. If there is already a connection, use that */
491 static NTSTATUS get_connection_from_cache(const char *domain, const char *pipe_name, struct winbindd_cm_conn **conn_out)
493 struct winbindd_cm_conn *conn, conn_temp;
496 for (conn = cm_conns; conn; conn = conn->next) {
497 if (strequal(conn->domain, domain) &&
498 strequal(conn->pipe_name, pipe_name)) {
499 if (!connection_ok(conn)) {
501 cli_shutdown(conn->cli);
503 ZERO_STRUCT(conn_temp);
504 conn_temp.next = conn->next;
505 DLIST_REMOVE(cm_conns, conn);
507 conn = &conn_temp; /* Just to keep the loop moving */
515 if (!(conn = malloc(sizeof(*conn))))
516 return NT_STATUS_NO_MEMORY;
520 if (!NT_STATUS_IS_OK(result = cm_open_connection(domain, pipe_name, conn))) {
521 DEBUG(3, ("Could not open a connection to %s for %s (%s)\n",
522 domain, pipe_name, nt_errstr(result)));
526 DLIST_ADD(cm_conns, conn);
533 /* Return a LSA policy handle on a domain */
535 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
537 struct winbindd_cm_conn *conn;
538 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
540 static CLI_POLICY_HND hnd;
542 /* Look for existing connections */
544 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
548 /* This *shitty* code needs scrapping ! JRA */
549 if (policy_handle_is_valid(&conn->pol)) {
555 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
556 des_access, &conn->pol);
558 if (!NT_STATUS_IS_OK(result)) {
559 /* Hit the cache code again. This cleans out the old connection and gets a new one */
560 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
561 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_LSARPC, &conn))) {
565 result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False,
566 des_access, &conn->pol);
569 if (!NT_STATUS_IS_OK(result)) {
570 cli_shutdown(conn->cli);
571 DLIST_REMOVE(cm_conns, conn);
583 /* Return a SAM policy handle on a domain */
585 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
587 struct winbindd_cm_conn *conn;
588 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
590 static CLI_POLICY_HND hnd;
592 /* Look for existing connections */
594 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
598 /* This *shitty* code needs scrapping ! JRA */
599 if (policy_handle_is_valid(&conn->pol)) {
604 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
605 des_access, &conn->pol);
607 if (!NT_STATUS_IS_OK(result)) {
608 /* Hit the cache code again. This cleans out the old connection and gets a new one */
609 if (conn->cli->fd == -1) { /* Try again, if the remote host disapeared */
610 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_SAMR, &conn))) {
614 result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
615 des_access, &conn->pol);
618 if (!NT_STATUS_IS_OK(result)) {
619 cli_shutdown(conn->cli);
620 DLIST_REMOVE(cm_conns, conn);
632 #if 0 /* This code now *well* out of date */
634 /* Return a SAM domain policy handle on a domain */
636 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
638 struct winbindd_cm_conn *conn, *basic_conn = NULL;
639 static CLI_POLICY_HND hnd;
641 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
643 /* Look for existing connections */
645 for (conn = cm_conns; conn; conn = conn->next) {
646 if (strequal(conn->domain, domain) &&
647 strequal(conn->pipe_name, PIPE_SAMR) &&
648 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
650 if (!connection_ok(conn)) {
651 /* Shutdown cli? Free conn? Allow retry of DC? */
652 DLIST_REMOVE(cm_conns, conn);
660 /* Create a basic handle to open a domain handle from */
662 if (!cm_get_sam_handle(domain))
665 for (conn = cm_conns; conn; conn = conn->next) {
666 if (strequal(conn->domain, domain) &&
667 strequal(conn->pipe_name, PIPE_SAMR) &&
668 conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
672 if (!(conn = (struct winbindd_cm_conn *)
673 malloc(sizeof(struct winbindd_cm_conn))))
678 fstrcpy(conn->domain, basic_conn->domain);
679 fstrcpy(conn->controller, basic_conn->controller);
680 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
682 conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
683 conn->cli = basic_conn->cli;
685 result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
686 &basic_conn->pol, des_access,
687 domain_sid, &conn->pol);
689 if (!NT_STATUS_IS_OK(result))
694 DLIST_ADD(cm_conns, conn);
703 /* Return a SAM policy handle on a domain user */
705 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
708 struct winbindd_cm_conn *conn, *basic_conn = NULL;
709 static CLI_POLICY_HND hnd;
711 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
713 /* Look for existing connections */
715 for (conn = cm_conns; conn; conn = conn->next) {
716 if (strequal(conn->domain, domain) &&
717 strequal(conn->pipe_name, PIPE_SAMR) &&
718 conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
719 conn->pipe_data.samr.rid == user_rid) {
721 if (!connection_ok(conn)) {
722 /* Shutdown cli? Free conn? Allow retry of DC? */
723 DLIST_REMOVE(cm_conns, conn);
731 /* Create a domain handle to open a user handle from */
733 if (!cm_get_sam_dom_handle(domain, domain_sid))
736 for (conn = cm_conns; conn; conn = conn->next) {
737 if (strequal(conn->domain, domain) &&
738 strequal(conn->pipe_name, PIPE_SAMR) &&
739 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
744 DEBUG(0, ("No domain sam handle was created!\n"));
748 if (!(conn = (struct winbindd_cm_conn *)
749 malloc(sizeof(struct winbindd_cm_conn))))
754 fstrcpy(conn->domain, basic_conn->domain);
755 fstrcpy(conn->controller, basic_conn->controller);
756 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
758 conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
759 conn->cli = basic_conn->cli;
760 conn->pipe_data.samr.rid = user_rid;
762 result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
763 &basic_conn->pol, des_access, user_rid,
766 if (!NT_STATUS_IS_OK(result))
771 DLIST_ADD(cm_conns, conn);
780 /* Return a SAM policy handle on a domain group */
782 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
785 struct winbindd_cm_conn *conn, *basic_conn = NULL;
786 static CLI_POLICY_HND hnd;
788 uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
790 /* Look for existing connections */
792 for (conn = cm_conns; conn; conn = conn->next) {
793 if (strequal(conn->domain, domain) &&
794 strequal(conn->pipe_name, PIPE_SAMR) &&
795 conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
796 conn->pipe_data.samr.rid == group_rid) {
798 if (!connection_ok(conn)) {
799 /* Shutdown cli? Free conn? Allow retry of DC? */
800 DLIST_REMOVE(cm_conns, conn);
808 /* Create a domain handle to open a user handle from */
810 if (!cm_get_sam_dom_handle(domain, domain_sid))
813 for (conn = cm_conns; conn; conn = conn->next) {
814 if (strequal(conn->domain, domain) &&
815 strequal(conn->pipe_name, PIPE_SAMR) &&
816 conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
821 DEBUG(0, ("No domain sam handle was created!\n"));
825 if (!(conn = (struct winbindd_cm_conn *)
826 malloc(sizeof(struct winbindd_cm_conn))))
831 fstrcpy(conn->domain, basic_conn->domain);
832 fstrcpy(conn->controller, basic_conn->controller);
833 fstrcpy(conn->pipe_name, basic_conn->pipe_name);
835 conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
836 conn->cli = basic_conn->cli;
837 conn->pipe_data.samr.rid = group_rid;
839 result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
840 &basic_conn->pol, des_access, group_rid,
843 if (!NT_STATUS_IS_OK(result))
848 DLIST_ADD(cm_conns, conn);
859 /* Get a handle on a netlogon pipe. This is a bit of a hack to re-use the
860 netlogon pipe as no handle is returned. */
862 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
863 struct cli_state **cli)
865 NTSTATUS result = NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND;
866 struct winbindd_cm_conn *conn;
867 uint32 neg_flags = 0x000001ff;
870 return NT_STATUS_INVALID_PARAMETER;
873 /* Open an initial conection */
875 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
879 result = cli_nt_setup_creds(conn->cli, get_sec_chan(), trust_passwd, &neg_flags, 2);
881 if (!NT_STATUS_IS_OK(result)) {
882 DEBUG(0, ("error connecting to domain password server: %s\n",
885 /* Hit the cache code again. This cleans out the old connection and gets a new one */
886 if (conn->cli->fd == -1) {
887 if (!NT_STATUS_IS_OK(result = get_connection_from_cache(domain, PIPE_NETLOGON, &conn))) {
892 result = cli_nt_setup_creds( conn->cli, get_sec_chan(),trust_passwd, &neg_flags, 2);
895 if (!NT_STATUS_IS_OK(result)) {
896 cli_shutdown(conn->cli);
897 DLIST_REMOVE(cm_conns, conn);
908 /* Dump the current connection status */
910 static void dump_conn_list(void)
912 struct winbindd_cm_conn *con;
914 DEBUG(0, ("\tDomain Controller Pipe\n"));
916 for(con = cm_conns; con; con = con->next) {
919 /* Display pipe info */
921 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
922 DEBUG(0, ("Error: not enough memory!\n"));
924 DEBUG(0, ("%s\n", msg));
930 void winbindd_cm_status(void)
932 /* List open connections */
934 DEBUG(0, ("winbindd connection manager status:\n"));
939 DEBUG(0, ("\tNo active connections\n"));