Much more useful handling of backup domain controllers in winbindd. Honour
[tprouty/samba.git] / source / nsswitch / winbindd_cm.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4
5    Winbind daemon connection manager
6
7    Copyright (C) Tim Potter 2001
8    
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.
13    
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.
18    
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.
22 */
23
24 /*
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:
28   
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
32          selection etc
33        - manage re-entrancy for when winbindd becomes able to handle
34          multiple outstanding rpc requests
35   
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.
42
43    The TNG design is quite good but I disagree with some aspects of the
44    implementation. -tpot
45
46  */
47
48 /*
49    TODO:
50
51      - I'm pretty annoyed by all the make_nmb_name() stuff.  It should be
52        moved down into another function.
53
54      - There needs to be a utility function in libsmb/namequery.c that does
55        cm_get_dc_name() 
56
57      - Take care when destroying cli_structs as they can be shared between
58        various sam handles.
59
60  */
61
62 #include "winbindd.h"
63
64 /* Global list of connections.  Initially a DLIST but can become a hash
65    table or whatever later. */
66
67 struct winbindd_cm_conn {
68         struct winbindd_cm_conn *prev, *next;
69         fstring domain;
70         fstring controller;
71         fstring pipe_name;
72         struct cli_state *cli;
73         POLICY_HND pol;
74 };
75
76 static struct winbindd_cm_conn *cm_conns = NULL;
77
78 /* Get a domain controller name.  Cache positive and negative lookups so we
79    don't go to the network too often when something is badly broken. */
80
81 #define GET_DC_NAME_CACHE_TIMEOUT 30 /* Seconds between dc lookups */
82
83 struct get_dc_name_cache {
84         fstring domain_name;
85         fstring srv_name;
86         time_t lookup_time;
87         struct get_dc_name_cache *prev, *next;
88 };
89
90 static BOOL cm_get_dc_name(char *domain, fstring srv_name)
91 {
92         static struct get_dc_name_cache *get_dc_name_cache;
93         struct get_dc_name_cache *dcc;
94         struct in_addr *ip_list, dc_ip;
95         int count, i = 0;
96
97         /* Check the cache for previous lookups */
98
99         for (dcc = get_dc_name_cache; dcc; dcc = dcc->next) {
100
101                 if (!strequal(domain, dcc->domain_name))
102                         continue; /* Not our domain */
103
104                 if ((time(NULL) - dcc->lookup_time) > 
105                     GET_DC_NAME_CACHE_TIMEOUT) {
106
107                         /* Cache entry has expired, delete it */
108
109                         DEBUG(10, ("get_dc_name_cache entry expired for %s\n", domain));
110
111                         DLIST_REMOVE(get_dc_name_cache, dcc);
112                         SAFE_FREE(dcc);
113
114                         break;
115                 }
116
117                 /* Return a positive or negative lookup for this domain */
118
119                 if (dcc->srv_name[0]) {
120                         DEBUG(10, ("returning positive get_dc_name_cache entry for %s\n", domain));
121                         fstrcpy(srv_name, dcc->srv_name);
122                         return True;
123                 } else {
124                         DEBUG(10, ("returning negative get_dc_name_cache entry for %s\n", domain));
125                         return False;
126                 }
127         }
128
129         /* Add cache entry for this lookup. */
130
131         DEBUG(10, ("Creating get_dc_name_cache entry for %s\n", domain));
132
133         if (!(dcc = (struct get_dc_name_cache *) 
134               malloc(sizeof(struct get_dc_name_cache))))
135                 return False;
136
137         ZERO_STRUCTP(dcc);
138
139         fstrcpy(dcc->domain_name, domain);
140         dcc->lookup_time = time(NULL);
141
142         DLIST_ADD(get_dc_name_cache, dcc);
143
144         /* Lookup domain controller name */
145                 
146         if (!get_dc_list(False, domain, &ip_list, &count)) {
147                 DEBUG(3, ("Could not look up dc's for domain %s\n", domain));
148                 return False;
149         }
150
151         /* Pick a nice close server */
152            
153         if (strequal(lp_passwordserver(), "*")) {
154                 
155                 /* Look for DC on local net */
156
157                 for (i = 0; i < count; i++) {
158                         if (is_local_net(ip_list[i]) &&
159                             name_status_find(domain, 0x1c, 0x20,
160                                              ip_list[i], srv_name)) {
161                                 dc_ip = ip_list[i];
162                                 goto done;
163                         }
164                         zero_ip(&ip_list[i]);
165                 }
166
167                 /* Look for other DCs */
168
169                 for (i = 0; i < count; i++) {
170                         if (!is_zero_ip(ip_list[i]) &&
171                             name_status_find(domain, 0x1c, 0x20,
172                                              ip_list[i], srv_name)) {
173                                 dc_ip = ip_list[i];
174                                 goto done;
175                         }
176                 }
177
178                 /* No-one to talk to )-: */
179
180                 return False;
181         }
182
183         /* Return first DC that we can contact */
184
185         for (i = 0; i < count; i++) {
186                 if (name_status_find(domain, 0x1c, 0x20, ip_list[i],
187                                      srv_name)) {
188                         dc_ip = ip_list[i];
189                         goto done;
190                 }
191         }
192
193         return False;           /* Boo-hoo */
194         
195  done:
196         /* We have the netbios name and IP address of a domain controller.
197            Ideally we should sent a SAMLOGON request to determine whether
198            the DC is alive and kicking.  If we can catch a dead DC before
199            performing a cli_connect() we can avoid a 30-second timeout. */
200
201         /* We have a name so make the cache entry positive now */
202
203         fstrcpy(dcc->srv_name, srv_name);
204
205         DEBUG(3, ("Returning DC %s (%s) for domain %s\n", srv_name,
206                   inet_ntoa(dc_ip), domain));
207
208         return True;
209 }
210
211 /* Choose between anonymous or authenticated connections.  We need to use
212    an authenticated connection if DCs have the RestrictAnonymous registry
213    entry set > 0, or the "Additional restrictions for anonymous
214    connections" set in the win2k Local Security Policy. */
215
216 void cm_init_creds(struct ntuser_creds *creds)
217 {
218         char *username, *password;
219
220         ZERO_STRUCTP(creds);
221
222         creds->pwd.null_pwd = True; /* anonymoose */
223
224         username = secrets_fetch(SECRETS_AUTH_USER, NULL);
225         password = secrets_fetch(SECRETS_AUTH_PASSWORD, NULL);
226
227         if (username && *username) {
228                 pwd_set_cleartext(&creds->pwd, password);
229
230                 fstrcpy(creds->user_name, username);
231                 fstrcpy(creds->domain, lp_workgroup());
232
233                 DEBUG(3, ("IPC$ connections done %s\\%s\n", creds->domain,
234                           creds->user_name));
235         } else 
236                 DEBUG(3, ("IPC$ connections done anonymously\n"));
237 }
238
239 /* Open a new smb pipe connection to a DC on a given domain.  Cache
240    negative creation attempts so we don't try and connect to broken
241    machines too often. */
242
243 #define OPEN_CONNECTION_CACHE_TIMEOUT 30 /* Seconds between attempts */
244
245 struct open_connection_cache {
246         fstring domain_name;
247         fstring controller;
248         time_t lookup_time;
249         struct open_connection_cache *prev, *next;
250 };
251
252 static BOOL cm_open_connection(char *domain, char *pipe_name,
253                                struct winbindd_cm_conn *new_conn)
254 {
255         static struct open_connection_cache *open_connection_cache;
256         struct open_connection_cache *occ;
257         struct nmb_name calling, called;
258         extern pstring global_myname;
259         fstring dest_host;
260         struct in_addr dest_ip;
261         BOOL result = False;
262         struct ntuser_creds creds;
263
264         fstrcpy(new_conn->domain, domain);
265         fstrcpy(new_conn->pipe_name, pipe_name);
266         
267         /* Look for a domain controller for this domain.  Negative results
268            are cached so don't bother applying the caching for this
269            function just yet.  */
270
271         if (!cm_get_dc_name(domain, new_conn->controller))
272                 goto done;
273
274         /* Return false if we have tried to look up this domain and netbios
275            name before and failed. */
276
277         for (occ = open_connection_cache; occ; occ = occ->next) {
278                 
279                 if (!(strequal(domain, occ->domain_name) &&
280                       strequal(new_conn->controller, occ->controller)))
281                         continue; /* Not our domain */
282
283                 if ((time(NULL) - occ->lookup_time) > 
284                     OPEN_CONNECTION_CACHE_TIMEOUT) {
285
286                         /* Cache entry has expired, delete it */
287
288                         DEBUG(10, ("cm_open_connection cache entry expired for %s, %s\n", domain, new_conn->controller));
289
290                         DLIST_REMOVE(open_connection_cache, occ);
291                         free(occ);
292
293                         break;
294                 }
295
296                 /* The timeout hasn't expired yet so return false */
297
298                 DEBUG(10, ("returning negative open_connection_cache entry for %s, %s\n", domain, new_conn->controller));
299
300                 goto done;
301         }
302
303         /* Initialise SMB connection */
304
305         if (!(new_conn->cli = cli_initialise(NULL)))
306                 goto done;
307
308         if (!resolve_srv_name(new_conn->controller, dest_host, &dest_ip))
309                 goto done;
310
311         make_nmb_name(&called, dns_to_netbios_name(new_conn->controller), 0x20);
312         make_nmb_name(&calling, dns_to_netbios_name(global_myname), 0);
313
314         cm_init_creds(&creds);
315
316         cli_init_creds(new_conn->cli, &creds);
317
318         if (!cli_establish_connection(new_conn->cli, new_conn->controller, 
319                                       &dest_ip, &calling, &called, "IPC$", 
320                                       "IPC", False, True))
321                 goto done;
322
323         if (!cli_nt_session_open (new_conn->cli, pipe_name))
324                 goto done;
325
326         result = True;
327
328  done:
329
330         /* Create negative lookup cache entry for this domain and controller */
331
332         if (!result) {
333                 if (!(occ = (struct open_connection_cache *)
334                       malloc(sizeof(struct open_connection_cache))))
335                         return False;
336
337                 ZERO_STRUCTP(occ);
338
339                 fstrcpy(occ->domain_name, domain);
340                 fstrcpy(occ->controller, new_conn->controller);
341                 occ->lookup_time = time(NULL);
342                 
343                 DLIST_ADD(open_connection_cache, occ);
344         }
345
346         if (!result && new_conn->cli)
347                 cli_shutdown(new_conn->cli);
348
349         return result;
350 }
351
352 /* Return true if a connection is still alive */
353
354 static BOOL connection_ok(struct winbindd_cm_conn *conn)
355 {
356         if (!conn->cli->initialised)
357                 return False;
358
359         if (conn->cli->fd == -1)
360                 return False;
361         
362         return True;
363 }
364
365 /* Return a LSA policy handle on a domain */
366
367 CLI_POLICY_HND *cm_get_lsa_handle(char *domain)
368 {
369         struct winbindd_cm_conn *conn;
370         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
371         NTSTATUS result;
372         static CLI_POLICY_HND hnd;
373
374         /* Look for existing connections */
375
376         for (conn = cm_conns; conn; conn = conn->next) {
377                 if (strequal(conn->domain, domain) && 
378                     strequal(conn->pipe_name, PIPE_LSARPC)) {
379
380                         if (!connection_ok(conn)) {
381                                 DLIST_REMOVE(cm_conns, conn);
382                                 return NULL;
383                         }
384
385                         goto ok;
386                 }
387         }
388
389         /* Create a new one */
390
391         if (!(conn = (struct winbindd_cm_conn *) malloc(sizeof(struct winbindd_cm_conn))))
392                 return NULL;
393
394         ZERO_STRUCTP(conn);
395
396         if (!cm_open_connection(domain, PIPE_LSARPC, conn)) {
397                 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
398                 return NULL;
399         }
400
401         result = cli_lsa_open_policy(conn->cli, conn->cli->mem_ctx, False, 
402                                      des_access, &conn->pol);
403
404         if (!NT_STATUS_IS_OK(result))
405                 return NULL;
406
407         /* Add to list */
408
409         DLIST_ADD(cm_conns, conn);
410
411  ok:
412         hnd.pol = conn->pol;
413         hnd.cli = conn->cli;
414
415         return &hnd;
416 }
417
418 /* Return a SAM policy handle on a domain */
419
420 CLI_POLICY_HND *cm_get_sam_handle(char *domain)
421
422         struct winbindd_cm_conn *conn;
423         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
424         NTSTATUS result;
425         static CLI_POLICY_HND hnd;
426
427         /* Look for existing connections */
428
429         for (conn = cm_conns; conn; conn = conn->next) {
430                 if (strequal(conn->domain, domain) && strequal(conn->pipe_name, PIPE_SAMR)) {
431
432                         if (!connection_ok(conn)) {
433                                 DLIST_REMOVE(cm_conns, conn);
434                                 return NULL;
435                         }
436
437                         goto ok;
438                 }
439         }
440
441         /* Create a new one */
442
443         if (!(conn = (struct winbindd_cm_conn *) 
444               malloc(sizeof(struct winbindd_cm_conn))))
445                 return NULL;
446
447         ZERO_STRUCTP(conn);
448
449         if (!cm_open_connection(domain, PIPE_SAMR, conn)) {
450                 DEBUG(3, ("Could not connect to a dc for domain %s\n", domain));
451                 return NULL;
452         }
453
454         result = cli_samr_connect(conn->cli, conn->cli->mem_ctx,
455                                   des_access, &conn->pol);
456
457         if (!NT_STATUS_IS_OK(result))
458                 return NULL;
459
460         /* Add to list */
461
462         DLIST_ADD(cm_conns, conn);
463
464  ok:
465         hnd.pol = conn->pol;
466         hnd.cli = conn->cli;
467
468         return &hnd;        
469 }
470
471 #if 0
472
473 /* Return a SAM domain policy handle on a domain */
474
475 CLI_POLICY_HND *cm_get_sam_dom_handle(char *domain, DOM_SID *domain_sid)
476 {
477         struct winbindd_cm_conn *conn, *basic_conn = NULL;
478         static CLI_POLICY_HND hnd;
479         NTSTATUS result;
480         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
481
482         /* Look for existing connections */
483
484         for (conn = cm_conns; conn; conn = conn->next) {
485                 if (strequal(conn->domain, domain) &&
486                     strequal(conn->pipe_name, PIPE_SAMR) &&
487                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM) {
488
489                         if (!connection_ok(conn)) {
490                                 DLIST_REMOVE(cm_conns, conn);
491                                 return NULL;
492                         }
493
494                         goto ok;
495                 }
496         }
497
498         /* Create a basic handle to open a domain handle from */
499
500         if (!cm_get_sam_handle(domain))
501                 return False;
502
503         for (conn = cm_conns; conn; conn = conn->next) {
504                 if (strequal(conn->domain, domain) &&
505                     strequal(conn->pipe_name, PIPE_SAMR) &&
506                     conn->pipe_data.samr.pipe_type == SAM_PIPE_BASIC)
507                         basic_conn = conn;
508         }
509         
510         if (!(conn = (struct winbindd_cm_conn *)
511               malloc(sizeof(struct winbindd_cm_conn))))
512                 return NULL;
513         
514         ZERO_STRUCTP(conn);
515
516         fstrcpy(conn->domain, basic_conn->domain);
517         fstrcpy(conn->controller, basic_conn->controller);
518         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
519
520         conn->pipe_data.samr.pipe_type = SAM_PIPE_DOM;
521         conn->cli = basic_conn->cli;
522
523         result = cli_samr_open_domain(conn->cli, conn->cli->mem_ctx,
524                                       &basic_conn->pol, des_access, 
525                                       domain_sid, &conn->pol);
526
527         if (!NT_STATUS_IS_OK(result))
528                 return NULL;
529
530         /* Add to list */
531
532         DLIST_ADD(cm_conns, conn);
533
534  ok:
535         hnd.pol = conn->pol;
536         hnd.cli = conn->cli;
537
538         return &hnd;
539 }
540
541 /* Return a SAM policy handle on a domain user */
542
543 CLI_POLICY_HND *cm_get_sam_user_handle(char *domain, DOM_SID *domain_sid,
544                                        uint32 user_rid)
545 {
546         struct winbindd_cm_conn *conn, *basic_conn = NULL;
547         static CLI_POLICY_HND hnd;
548         NTSTATUS result;
549         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
550
551         /* Look for existing connections */
552
553         for (conn = cm_conns; conn; conn = conn->next) {
554                 if (strequal(conn->domain, domain) &&
555                     strequal(conn->pipe_name, PIPE_SAMR) &&
556                     conn->pipe_data.samr.pipe_type == SAM_PIPE_USER &&
557                     conn->pipe_data.samr.rid == user_rid) {
558
559                         if (!connection_ok(conn)) {
560                                 DLIST_REMOVE(cm_conns, conn);
561                                 return NULL;
562                         }
563                 
564                         goto ok;
565                 }
566         }
567
568         /* Create a domain handle to open a user handle from */
569
570         if (!cm_get_sam_dom_handle(domain, domain_sid))
571                 return NULL;
572
573         for (conn = cm_conns; conn; conn = conn->next) {
574                 if (strequal(conn->domain, domain) &&
575                     strequal(conn->pipe_name, PIPE_SAMR) &&
576                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
577                         basic_conn = conn;
578         }
579         
580         if (!basic_conn) {
581                 DEBUG(0, ("No domain sam handle was created!\n"));
582                 return NULL;
583         }
584
585         if (!(conn = (struct winbindd_cm_conn *)
586               malloc(sizeof(struct winbindd_cm_conn))))
587                 return NULL;
588         
589         ZERO_STRUCTP(conn);
590
591         fstrcpy(conn->domain, basic_conn->domain);
592         fstrcpy(conn->controller, basic_conn->controller);
593         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
594         
595         conn->pipe_data.samr.pipe_type = SAM_PIPE_USER;
596         conn->cli = basic_conn->cli;
597         conn->pipe_data.samr.rid = user_rid;
598
599         result = cli_samr_open_user(conn->cli, conn->cli->mem_ctx,
600                                     &basic_conn->pol, des_access, user_rid,
601                                     &conn->pol);
602
603         if (!NT_STATUS_IS_OK(result))
604                 return NULL;
605
606         /* Add to list */
607
608         DLIST_ADD(cm_conns, conn);
609
610  ok:
611         hnd.pol = conn->pol;
612         hnd.cli = conn->cli;
613
614         return &hnd;
615 }
616
617 /* Return a SAM policy handle on a domain group */
618
619 CLI_POLICY_HND *cm_get_sam_group_handle(char *domain, DOM_SID *domain_sid,
620                                         uint32 group_rid)
621 {
622         struct winbindd_cm_conn *conn, *basic_conn = NULL;
623         static CLI_POLICY_HND hnd;
624         NTSTATUS result;
625         uint32 des_access = SEC_RIGHTS_MAXIMUM_ALLOWED;
626
627         /* Look for existing connections */
628
629         for (conn = cm_conns; conn; conn = conn->next) {
630                 if (strequal(conn->domain, domain) &&
631                     strequal(conn->pipe_name, PIPE_SAMR) &&
632                     conn->pipe_data.samr.pipe_type == SAM_PIPE_GROUP &&
633                     conn->pipe_data.samr.rid == group_rid) {
634
635                         if (!connection_ok(conn)) {
636                                 DLIST_REMOVE(cm_conns, conn);
637                                 return NULL;
638                         }
639                 
640                         goto ok;
641                 }
642         }
643
644         /* Create a domain handle to open a user handle from */
645
646         if (!cm_get_sam_dom_handle(domain, domain_sid))
647                 return NULL;
648
649         for (conn = cm_conns; conn; conn = conn->next) {
650                 if (strequal(conn->domain, domain) &&
651                     strequal(conn->pipe_name, PIPE_SAMR) &&
652                     conn->pipe_data.samr.pipe_type == SAM_PIPE_DOM)
653                         basic_conn = conn;
654         }
655         
656         if (!basic_conn) {
657                 DEBUG(0, ("No domain sam handle was created!\n"));
658                 return NULL;
659         }
660
661         if (!(conn = (struct winbindd_cm_conn *)
662               malloc(sizeof(struct winbindd_cm_conn))))
663                 return NULL;
664         
665         ZERO_STRUCTP(conn);
666
667         fstrcpy(conn->domain, basic_conn->domain);
668         fstrcpy(conn->controller, basic_conn->controller);
669         fstrcpy(conn->pipe_name, basic_conn->pipe_name);
670         
671         conn->pipe_data.samr.pipe_type = SAM_PIPE_GROUP;
672         conn->cli = basic_conn->cli;
673         conn->pipe_data.samr.rid = group_rid;
674
675         result = cli_samr_open_group(conn->cli, conn->cli->mem_ctx,
676                                     &basic_conn->pol, des_access, group_rid,
677                                     &conn->pol);
678
679         if (!NT_STATUS_IS_OK(result))
680                 return NULL;
681
682         /* Add to list */
683
684         DLIST_ADD(cm_conns, conn);
685
686  ok:
687         hnd.pol = conn->pol;
688         hnd.cli = conn->cli;
689
690         return &hnd;
691 }
692
693 #endif
694
695 /* Get a handle on a netlogon pipe.  This is a bit of a hack to re-use the
696    netlogon pipe as no handle is returned. */
697
698 NTSTATUS cm_get_netlogon_cli(char *domain, unsigned char *trust_passwd,
699                              struct cli_state **cli)
700 {
701         struct winbindd_cm_conn conn;
702         NTSTATUS result = NT_STATUS_UNSUCCESSFUL;
703
704         /* Open an initial conection */
705
706         ZERO_STRUCT(conn);
707
708         if (!cm_open_connection(domain, PIPE_NETLOGON, &conn)) {
709                 DEBUG(3, ("Could not open a connection to %s\n", domain));
710                 return result;
711         }
712
713         result = new_cli_nt_setup_creds(conn.cli, trust_passwd);
714
715         if (!NT_STATUS_IS_OK(result)) {
716                 DEBUG(0, ("error connecting to domain password server: %s\n",
717                         get_nt_error_msg(result)));
718                         cli_shutdown(conn.cli);
719                         return result;
720         }
721
722         if (cli)
723                 *cli = conn.cli;
724
725         return result;
726 }
727
728 /* Dump the current connection status */
729
730 static void dump_conn_list(void)
731 {
732         struct winbindd_cm_conn *con;
733
734         DEBUG(0, ("\tDomain          Controller      Pipe\n"));
735
736         for(con = cm_conns; con; con = con->next) {
737                 char *msg;
738
739                 /* Display pipe info */
740                 
741                 if (asprintf(&msg, "\t%-15s %-15s %-16s", con->domain, con->controller, con->pipe_name) < 0) {
742                         DEBUG(0, ("Error: not enough memory!\n"));
743                 } else {
744                         DEBUG(0, ("%s\n", msg));
745                         SAFE_FREE(msg);
746                 }
747         }
748 }
749
750 void winbindd_cm_status(void)
751 {
752         /* List open connections */
753
754         DEBUG(0, ("winbindd connection manager status:\n"));
755
756         if (cm_conns)
757                 dump_conn_list();
758         else
759                 DEBUG(0, ("\tNo active connections\n"));
760 }