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