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