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