]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/netncp/ncp_conn.c
This commit was generated by cvs2svn to compensate for changes in r145557,
[FreeBSD/FreeBSD.git] / sys / netncp / ncp_conn.c
1 /*-
2  * Copyright (c) 1999, Boris Popov
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *    This product includes software developed by Boris Popov.
16  * 4. Neither the name of the author nor the names of any co-contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * Connection tables
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/proc.h>
43 #include <sys/lock.h>
44 #include <sys/sysctl.h>
45
46 #include <netncp/ncp.h>
47 #include <netncp/nwerror.h>
48 #include <netncp/ncp_subr.h>
49 #include <netncp/ncp_conn.h>
50 #include <netncp/ncp_sock.h>
51 #include <netncp/ncp_ncp.h>
52
53 SLIST_HEAD(ncp_handle_head,ncp_handle);
54
55 int ncp_burst_enabled = 1;
56
57 struct ncp_conn_head conn_list={NULL};
58 static int ncp_conn_cnt = 0;
59 static int ncp_next_ref = 1;
60 static struct lock listlock;
61
62 struct ncp_handle_head lhlist={NULL};
63 static int ncp_next_handle = 1;
64 static struct lock lhlock;
65
66 static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS);
67 static int ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td,
68     struct ucred *cred);
69
70 SYSCTL_DECL(_net_ncp);
71 SYSCTL_INT (_net_ncp, OID_AUTO, burst_enabled, CTLFLAG_RD, &ncp_burst_enabled, 0, "");
72 SYSCTL_INT (_net_ncp, OID_AUTO, conn_cnt, CTLFLAG_RD, &ncp_conn_cnt, 0, "");
73 SYSCTL_PROC(_net_ncp, OID_AUTO, conn_stat, CTLFLAG_RD|CTLTYPE_OPAQUE,
74             NULL, 0, ncp_sysctl_connstat, "S,connstat", "Connections list");
75
76 MALLOC_DEFINE(M_NCPDATA, "NCP data", "NCP private data");
77
78 int
79 ncp_conn_init(void)
80 {
81         lockinit(&listlock, PSOCK, "ncpll", 0, 0);
82         lockinit(&lhlock, PSOCK, "ncplh", 0, 0);
83         return 0;
84 }
85
86 int
87 ncp_conn_destroy(void)
88 {
89         if (ncp_conn_cnt) {
90                 NCPERROR("There are %d connections active\n", ncp_conn_cnt);
91                 return EBUSY;
92         }
93         lockdestroy(&listlock);
94         lockdestroy(&lhlock);
95         return 0;
96 }
97
98 int
99 ncp_conn_locklist(int flags, struct thread *td)
100 {
101         return lockmgr(&listlock, flags | LK_CANRECURSE, 0, td);
102 }
103
104 void
105 ncp_conn_unlocklist(struct thread *td)
106 {
107         lockmgr(&listlock, LK_RELEASE, 0, td);
108 }
109
110 int
111 ncp_conn_access(struct ncp_conn *conn, struct ucred *cred, mode_t mode)
112 {
113         int error;
114
115         if (cred == NOCRED || ncp_suser(cred) == 0 ||
116             cred->cr_uid == conn->nc_owner->cr_uid)
117                 return 0;
118         mode >>= 3;
119         if (!groupmember(conn->nc_group, cred))
120                 mode >>= 3;
121         error = (conn->li.access_mode & mode) == mode ? 0 : EACCES;
122         return error;
123 }
124
125 int
126 ncp_conn_lock_any(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
127 {
128         int error;
129
130         if (conn->nc_id == 0) return EACCES;
131         error = lockmgr(&conn->nc_lock, LK_EXCLUSIVE | LK_CANRECURSE, 0, td);
132         if (error == ERESTART)
133                 return EINTR;
134         error = ncp_chkintr(conn, td);
135         if (error) {
136                 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
137                 return error;
138         }
139
140         if (conn->nc_id == 0) {
141                 lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
142                 return EACCES;
143         }
144         conn->td = td;          /* who currently operates */
145         conn->ucred = cred;
146         return 0;
147 }
148
149 int
150 ncp_conn_lock(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
151 {
152         int error;
153
154         error = ncp_conn_access(conn, cred, mode);
155         if (error) return error;
156         return ncp_conn_lock_any(conn, td, cred);
157 }
158
159 /*
160  * Lock conn but unlock connlist
161  */
162 static int
163 ncp_conn_lock2(struct ncp_conn *conn, struct thread *td, struct ucred *cred, int mode)
164 {
165         int error;
166
167         error = ncp_conn_access(conn, cred, mode);
168         if (error) {
169                 ncp_conn_unlocklist(td);
170                 return error;
171         }
172         conn->nc_lwant++;
173         ncp_conn_unlocklist(td);
174         error = ncp_conn_lock_any(conn, td, cred);
175         conn->nc_lwant--;
176         if (conn->nc_lwant == 0) {
177                 wakeup(&conn->nc_lwant);
178         }
179         return error;
180 }
181
182 void
183 ncp_conn_unlock(struct ncp_conn *conn, struct thread *td)
184 {
185         /*
186          * note, that LK_RELASE will do wakeup() instead of wakeup_one().
187          * this will do a little overhead
188          */
189         lockmgr(&conn->nc_lock, LK_RELEASE, 0, td);
190 }
191
192 int
193 ncp_conn_assert_locked(struct ncp_conn *conn, const char *checker, struct thread *td)
194 {
195         if (conn->nc_lock.lk_flags & LK_HAVE_EXCL) return 0;
196         printf("%s: connection isn't locked!\n", checker);
197         return EIO;
198 }
199
200 void
201 ncp_conn_invalidate(struct ncp_conn *ncp)
202 {
203         ncp->flags &= ~(NCPFL_ATTACHED | NCPFL_LOGGED | NCPFL_INVALID);
204 }
205
206 int
207 ncp_conn_invalid(struct ncp_conn *ncp)
208 {
209         return ncp->flags & NCPFL_INVALID;
210 }
211
212 /*
213  * create, fill with defaults and return in locked state
214  */
215 int
216 ncp_conn_alloc(struct ncp_conn_args *cap, struct thread *td, struct ucred *cred,
217         struct ncp_conn **conn)
218 {
219         struct ncp_conn *ncp;
220         struct ucred *owner;
221         int error, isroot;
222
223         if (cap->saddr.sa_family != AF_INET && cap->saddr.sa_family != AF_IPX)
224                 return EPROTONOSUPPORT;
225         isroot = ncp_suser(cred) == 0;
226         /*
227          * Only root can change ownership
228          */
229         if (cap->owner != NCP_DEFAULT_OWNER && !isroot)
230                 return EPERM;
231         if (cap->group != NCP_DEFAULT_GROUP &&
232             !groupmember(cap->group, cred) && !isroot)
233                 return EPERM;
234         if (cap->owner != NCP_DEFAULT_OWNER) {
235                 owner = crget();
236                 owner->cr_uid = cap->owner;
237         } else
238                 owner = crhold(cred);
239         MALLOC(ncp, struct ncp_conn *, sizeof(struct ncp_conn), 
240             M_NCPDATA, M_WAITOK | M_ZERO);
241         error = 0;
242         lockinit(&ncp->nc_lock, PZERO, "ncplck", 0, 0);
243         ncp_conn_cnt++;
244         ncp->nc_id = ncp_next_ref++;
245         ncp->nc_owner = cred;
246         ncp->seq = 0;
247         ncp->connid = 0xFFFF;
248         ncp->li = *cap;
249         ncp->nc_group = (cap->group != NCP_DEFAULT_GROUP) ?
250                 cap->group : cred->cr_groups[0];
251
252         if (cap->retry_count == 0)
253                 ncp->li.retry_count = NCP_RETRY_COUNT;
254         if (cap->timeout == 0)
255                 ncp->li.timeout = NCP_RETRY_TIMEOUT;
256         ncp_conn_lock_any(ncp, td, ncp->nc_owner);
257         *conn = ncp;
258         ncp_conn_locklist(LK_EXCLUSIVE, td);
259         SLIST_INSERT_HEAD(&conn_list,ncp,nc_next);
260         ncp_conn_unlocklist(td);
261         return (error);
262 }
263
264 /*
265  * Remove the connection, on entry it must be locked
266  */
267 int
268 ncp_conn_free(struct ncp_conn *ncp)
269 {
270         struct thread *td;
271         int error;
272
273         if (ncp == NULL) {
274                 NCPFATAL("ncp == NULL\n");
275                 return 0;
276         }
277         if (ncp->nc_id == 0) {
278                 NCPERROR("nc_id == 0\n");
279                 return EACCES;
280         }
281         td = ncp->td;
282         error = ncp_conn_assert_locked(ncp, __func__, td);
283         if (error)
284                 return error;
285         if (ncp->ref_cnt != 0 || (ncp->flags & NCPFL_PERMANENT))
286                 return EBUSY;
287         if (ncp_conn_access(ncp, ncp->ucred, NCPM_WRITE))
288                 return EACCES;
289
290         if (ncp->flags & NCPFL_ATTACHED)
291                 ncp_ncp_disconnect(ncp);
292         ncp_sock_disconnect(ncp);
293
294         /*
295          * Mark conn as dead and wait for other process
296          */
297         ncp->nc_id = 0;
298         ncp_conn_unlock(ncp, td);
299         /*
300          * if signal is raised - how I do react ?
301          */
302         lockmgr(&ncp->nc_lock, LK_DRAIN, 0, td);
303         lockdestroy(&ncp->nc_lock);
304         while (ncp->nc_lwant) {
305                 printf("lwant = %d\n", ncp->nc_lwant);
306                 tsleep(&ncp->nc_lwant, PZERO,"ncpdr",2*hz);
307         }
308         ncp_conn_locklist(LK_EXCLUSIVE, td);
309         SLIST_REMOVE(&conn_list, ncp, ncp_conn, nc_next);
310         ncp_conn_cnt--;
311         ncp_conn_unlocklist(td);
312         if (ncp->li.user)
313                 free(ncp->li.user, M_NCPDATA);
314         if (ncp->li.password)
315                 free(ncp->li.password, M_NCPDATA);
316         crfree(ncp->nc_owner);
317         FREE(ncp, M_NCPDATA);
318         return (0);
319 }
320
321 int
322 ncp_conn_reconnect(struct ncp_conn *ncp)
323 {
324         int error;
325
326         /*
327          * Close opened sockets if any
328          */
329         ncp_sock_disconnect(ncp);
330         error = ncp_sock_connect(ncp);
331         if (error)
332                 return error;
333         error = ncp_ncp_connect(ncp);
334         if (error)
335                 return error;
336         error = ncp_renegotiate_connparam(ncp, NCP_DEFAULT_BUFSIZE, 0);
337         if (error == NWE_SIGNATURE_LEVEL_CONFLICT) {
338                 printf("Unable to negotiate requested security level\n");
339                 error = EOPNOTSUPP;
340         }
341         if (error) {
342                 ncp_ncp_disconnect(ncp);
343                 return error;
344         }
345 #ifdef NCPBURST
346         error = ncp_burst_connect(ncp);
347         if (error) {
348                 ncp_ncp_disconnect(ncp);
349                 return error;
350         }
351 #endif
352         return 0;
353 }
354
355 int
356 ncp_conn_login(struct ncp_conn *conn, struct thread *td, struct ucred *cred)
357 {
358         struct ncp_bindery_object user;
359         u_char ncp_key[8];
360         int error;
361
362         error = ncp_get_encryption_key(conn, ncp_key);
363         if (error) {
364                 printf("%s: Warning: use unencrypted login\n", __func__);
365                 error = ncp_login_unencrypted(conn, conn->li.objtype,
366                     conn->li.user, conn->li.password, td, cred);
367         } else {
368                 error = ncp_get_bindery_object_id(conn, conn->li.objtype,
369                     conn->li.user, &user, td, cred);
370                 if (error)
371                         return error;
372                 error = ncp_login_encrypted(conn, &user, ncp_key,
373                     conn->li.password, td, cred);
374         }
375         if (!error)
376                 conn->flags |= NCPFL_LOGGED | NCPFL_WASLOGGED;
377         return error;
378 }
379
380 /*
381  * Lookup connection by handle, return a locked conn descriptor
382  */
383 int
384 ncp_conn_getbyref(int ref, struct thread *td, struct ucred *cred, int mode,
385                   struct ncp_conn **connpp)
386 {
387         struct ncp_conn *ncp;
388         int error = 0;
389
390         ncp_conn_locklist(LK_SHARED, td);
391         SLIST_FOREACH(ncp, &conn_list, nc_next)
392                 if (ncp->nc_id == ref) break;
393         if (ncp == NULL) {
394                 ncp_conn_unlocklist(td);
395                 return(EBADF);
396         }
397         error = ncp_conn_lock2(ncp, td, cred, mode);
398         if (!error)
399                 *connpp = ncp;
400         return (error);
401 }
402 /*
403  * find attached, but not logged in connection to specified server
404  */
405 int
406 ncp_conn_getattached(struct ncp_conn_args *li, struct thread *td,
407                      struct ucred *cred, int mode, struct ncp_conn **connpp)
408 {
409         struct ncp_conn *ncp, *ncp2 = NULL;
410         int error = 0;
411
412         ncp_conn_locklist(LK_SHARED, td);
413         SLIST_FOREACH(ncp, &conn_list, nc_next) {
414                 if ((ncp->flags & NCPFL_LOGGED) != 0 ||
415                     strcmp(ncp->li.server,li->server) != 0 ||
416                     ncp->li.saddr.sa_len != li->saddr.sa_len ||
417                     bcmp(&ncp->li.saddr,&ncp->li.saddr,li->saddr.sa_len) != 0)
418                         continue;
419                 if (ncp_suser(cred) == 0 ||
420                     cred->cr_uid == ncp->nc_owner->cr_uid)
421                         break;
422                 error = ncp_conn_access(ncp,cred,mode);
423                 if (!error && ncp2 == NULL)
424                         ncp2 = ncp;
425         }
426         if (ncp == NULL) ncp = ncp2;
427         if (ncp == NULL) {
428                 ncp_conn_unlocklist(td);
429                 return(EBADF);
430         }
431         error = ncp_conn_lock2(ncp, td, cred, mode);
432         if (!error)
433                 *connpp=ncp;
434         return (error);
435 }
436
437 /*
438  * Lookup connection by server/user pair, return a locked conn descriptor.
439  * if li is NULL or server/user pair incomplete, try to select best connection
440  * based on owner.
441  * Connection selected in next order:
442  * 1. Try to search conn with ucred owner, if li is NULL also find a primary
443  * 2. If 1. fails try to get first suitable shared connection
444  * 3. If 2. fails then nothing can help to poor ucred owner
445  */
446
447 int
448 ncp_conn_getbyli(struct ncp_conn_args *li, struct thread *td,
449                  struct ucred *cred, int mode, struct ncp_conn **connpp)
450 {
451         struct ncp_conn *ncp, *ncp2 = NULL;
452         int error = 0, partial, haveserv;
453
454         partial = (li == NULL || li->server[0] == 0 || li->user == NULL);
455         haveserv = (li && li->server[0]);
456         ncp_conn_locklist(LK_SHARED, td);
457         SLIST_FOREACH(ncp, &conn_list, nc_next) {
458                 if (partial) {
459                         if (cred->cr_uid == ncp->nc_owner->cr_uid) {
460                                 if (haveserv) {
461                                         if (strcmp(ncp->li.server,li->server) == 0)
462                                                 break;
463                                 } else {
464                                         if (ncp->flags & NCPFL_PRIMARY)
465                                                 break;
466                                         ncp2 = ncp;
467                                 }
468                                 continue;
469                         }
470                 } else {
471                         if (strcmp(ncp->li.server,li->server) != 0 || 
472                             ncp->li.user == NULL ||
473                             strcmp(ncp->li.user,li->user) != 0)
474                                 continue;
475                         if (cred->cr_uid == ncp->nc_owner->cr_uid)
476                                 break;
477                         if (ncp_suser(cred) == 0)
478                                 ncp2 = ncp;
479                 }
480                 error = ncp_conn_access(ncp,cred,mode);
481                 if (!error && ncp2 == NULL)
482                         ncp2 = ncp;
483         }
484         if (ncp == NULL) ncp = ncp2;
485         if (ncp == NULL) {
486                 ncp_conn_unlocklist(td);
487                 return(EBADF);
488         }
489         error = ncp_conn_lock2(ncp, td, cred,mode);
490         if (!error)
491                 *connpp=ncp;
492         return (error);
493 }
494
495 /*
496  * Set primary connection flag, since it have sence only for an owner,
497  * only owner can modify this flag.
498  * connection expected to be locked.
499  */
500 int
501 ncp_conn_setprimary(struct ncp_conn *conn, int on)
502 {
503         struct ncp_conn *ncp=NULL;
504
505         if (conn->ucred->cr_uid != conn->nc_owner->cr_uid)
506                 return EACCES;
507         ncp_conn_locklist(LK_SHARED, conn->td);
508         SLIST_FOREACH(ncp, &conn_list, nc_next) {
509                 if (conn->ucred->cr_uid == ncp->nc_owner->cr_uid)
510                         ncp->flags &= ~NCPFL_PRIMARY;
511         }
512         ncp_conn_unlocklist(conn->td);
513         if (on)
514                 conn->flags |= NCPFL_PRIMARY;
515         return 0;
516 }
517 /*
518  * Lease conn to given proc, returning unique handle
519  * problem: how locks should be applied ?
520  */
521 int
522 ncp_conn_gethandle(struct ncp_conn *conn, struct thread *td, struct ncp_handle **handle)
523 {
524         struct ncp_handle *refp;
525
526         lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
527         SLIST_FOREACH(refp, &lhlist, nh_next)
528                 if (refp->nh_conn == conn && td == refp->nh_td) break;
529         if (refp) {
530                 conn->ref_cnt++;
531                 refp->nh_ref++;
532                 *handle = refp;
533                 lockmgr(&lhlock, LK_RELEASE, 0, td);
534                 return 0;
535         }
536         MALLOC(refp,struct ncp_handle *,sizeof(struct ncp_handle),M_NCPDATA,
537             M_WAITOK | M_ZERO);
538         SLIST_INSERT_HEAD(&lhlist,refp,nh_next);
539         refp->nh_ref++;
540         refp->nh_td = td;
541         refp->nh_conn = conn;
542         refp->nh_id = ncp_next_handle++;
543         *handle = refp;
544         conn->ref_cnt++;
545         lockmgr(&lhlock, LK_RELEASE, 0, td);
546         return 0;
547 }
548 /*
549  * release reference, if force - ignore refcount
550  */
551 int
552 ncp_conn_puthandle(struct ncp_handle *handle, struct thread *td, int force)
553 {
554         struct ncp_handle *refp = handle;
555
556         lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
557         refp->nh_ref--;
558         refp->nh_conn->ref_cnt--;
559         if (force) {
560                 refp->nh_conn->ref_cnt -= refp->nh_ref;
561                 refp->nh_ref = 0;
562         }
563         if (refp->nh_ref == 0) {
564                 SLIST_REMOVE(&lhlist, refp, ncp_handle, nh_next);
565                 FREE(refp, M_NCPDATA);
566         }
567         lockmgr(&lhlock, LK_RELEASE, 0, td);
568         return 0;
569 }
570 /*
571  * find a connHandle
572  */
573 int
574 ncp_conn_findhandle(int connHandle, struct thread *td, struct ncp_handle **handle) {
575         struct ncp_handle *refp;
576
577         lockmgr(&lhlock, LK_SHARED, 0, td);
578         SLIST_FOREACH(refp, &lhlist, nh_next)
579                 if (refp->nh_td == td && refp->nh_id == connHandle) break;
580         lockmgr(&lhlock, LK_RELEASE, 0, td);
581         if (refp == NULL) {
582                 return EBADF;
583         }
584         *handle = refp;
585         return 0;
586 }
587 /*
588  * Clear handles associated with specified process
589  */
590 int
591 ncp_conn_putprochandles(struct thread *td)
592 {
593         struct ncp_handle *hp, *nhp;
594         int haveone = 0;
595
596         lockmgr(&lhlock, LK_EXCLUSIVE, 0, td);
597         for (hp = SLIST_FIRST(&lhlist); hp; hp = nhp) {
598                 nhp = SLIST_NEXT(hp, nh_next);
599                 if (hp->nh_td != td) continue;
600                 haveone = 1;
601                 hp->nh_conn->ref_cnt -= hp->nh_ref;
602                 SLIST_REMOVE(&lhlist, hp, ncp_handle, nh_next);
603                 FREE(hp, M_NCPDATA);
604         }
605         lockmgr(&lhlock, LK_RELEASE, 0, td);
606         return haveone;
607 }
608 /*
609  * remove references in all possible connections,
610  * XXX - possible problem is a locked list.
611  */
612 /*void
613 ncp_conn_list_rm_ref(pid_t pid) {
614         struct ncp_conn *ncp;
615
616         ncp_conn_locklist(LK_SHARED, NULL);
617         SLIST_FOREACH(ncp, &conn_list, nc_next) {
618                 ncp_conn_rm_ref(ncp,pid,1);
619         }
620         ncp_conn_unlocklist(NULL);
621         return;
622 }
623 */
624 int
625 ncp_conn_getinfo(struct ncp_conn *ncp, struct ncp_conn_stat *ncs) {
626         bzero(ncs,sizeof(*ncs));
627         ncs->li = ncp->li;
628         ncs->li.user = ncs->user;
629         if (ncp->li.user)
630                 strcpy(ncs->user, ncp->li.user);
631         ncs->li.password = NULL;
632         ncs->connRef = ncp->nc_id;
633         ncs->ref_cnt = ncp->ref_cnt;
634         ncs->connid = ncp->connid;
635         ncs->owner = ncp->nc_owner->cr_uid;
636         ncs->group = ncp->nc_group;
637         ncs->flags = ncp->flags;
638         ncs->buffer_size = ncp->buffer_size;
639         return 0;
640 }
641
642 static int
643 ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS)
644 {
645         int error;
646         struct ncp_conn_stat ncs;
647         struct ncp_conn *ncp;
648 /*      struct ucred *cred = req->td->td_ucred;*/
649
650         error = sysctl_wire_old_buffer(req, 0);
651         if (error != 0)
652                 return (error);
653         ncp_conn_locklist(LK_SHARED, req->td);
654         error = SYSCTL_OUT(req, &ncp_conn_cnt, sizeof(ncp_conn_cnt));
655         SLIST_FOREACH(ncp, &conn_list, nc_next) {
656                 if (error) break;
657                 /* I can't do conn_lock while list is locked */
658                 ncp->nc_lwant++;
659                 if (!error) {
660                         ncp_conn_getinfo(ncp, &ncs);
661                 } else {
662                         bzero(&ncs,sizeof(ncs));
663                         ncs.connRef = ncp->nc_id;
664                         strcpy(ncs.li.server,"***");
665                 }
666                 ncp->nc_lwant--;
667                 error = SYSCTL_OUT(req, &ncs, sizeof(ncs));
668         }
669         ncp_conn_unlocklist(req->td);
670         return(error);
671 }