2 * Copyright (c) 1999, Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
31 * $FreeBSD: src/sys/netncp/ncp_conn.c,v 1.3.2.5 2001/02/22 08:54:11 bp Exp $
32 * $DragonFly: src/sys/netproto/ncp/ncp_conn.c,v 1.9 2006/03/02 19:07:59 dillon Exp $
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
42 #include <sys/sysctl.h>
48 SLIST_HEAD(ncp_handle_head
,ncp_handle
);
50 int ncp_burst_enabled
= 1;
52 struct ncp_conn_head conn_list
={NULL
};
53 static int ncp_conn_cnt
= 0;
54 static int ncp_next_ref
= 1;
55 static struct lock listlock
;
57 struct ncp_handle_head lhlist
={NULL
};
58 static int ncp_next_handle
= 1;
59 static struct lock lhlock
;
61 static int ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS
);
62 static int ncp_conn_lock_any(struct ncp_conn
*conn
, struct thread
*td
,
65 SYSCTL_DECL(_net_ncp
);
66 SYSCTL_INT (_net_ncp
, OID_AUTO
, burst_enabled
, CTLFLAG_RD
, &ncp_burst_enabled
, 0, "");
67 SYSCTL_INT (_net_ncp
, OID_AUTO
, conn_cnt
, CTLFLAG_RD
, &ncp_conn_cnt
, 0, "");
68 SYSCTL_PROC(_net_ncp
, OID_AUTO
, conn_stat
, CTLFLAG_RD
|CTLTYPE_OPAQUE
,
69 NULL
, 0, ncp_sysctl_connstat
, "S,connstat", "Connections list");
71 MALLOC_DEFINE(M_NCPDATA
, "NCP data", "NCP private data");
75 lockinit(&listlock
, "ncpll", 0, 0);
76 lockinit(&lhlock
, "ncplh", 0, 0);
81 ncp_conn_locklist(int flags
, struct thread
*td
)
83 return lockmgr(&listlock
, flags
| LK_CANRECURSE
, NULL
, td
);
87 ncp_conn_unlocklist(struct thread
*td
)
89 lockmgr(&listlock
, LK_RELEASE
, NULL
, td
);
93 ncp_conn_access(struct ncp_conn
*conn
, struct ucred
*cred
, mode_t mode
)
97 if (cred
== NOCRED
|| ncp_suser(cred
) == 0 ||
98 cred
->cr_uid
== conn
->nc_owner
->cr_uid
)
101 if (!groupmember(conn
->nc_group
, cred
))
103 error
= (conn
->li
.access_mode
& mode
) == mode
? 0 : EACCES
;
108 ncp_conn_lock_any(struct ncp_conn
*conn
, struct thread
*td
, struct ucred
*cred
)
112 if (conn
->nc_id
== 0) return EACCES
;
113 error
= lockmgr(&conn
->nc_lock
, LK_EXCLUSIVE
| LK_CANRECURSE
, NULL
, td
);
114 if (error
== ERESTART
)
116 error
= ncp_chkintr(conn
, td
);
118 lockmgr(&conn
->nc_lock
, LK_RELEASE
, NULL
, td
);
122 if (conn
->nc_id
== 0) {
123 lockmgr(&conn
->nc_lock
, LK_RELEASE
, NULL
, td
);
126 conn
->td
= td
; /* who currently operates */
132 ncp_conn_lock(struct ncp_conn
*conn
, struct thread
*td
,
133 struct ucred
*cred
, int mode
)
137 error
= ncp_conn_access(conn
, cred
, mode
);
140 return ncp_conn_lock_any(conn
, td
, cred
);
144 * Lock conn but unlock connlist
147 ncp_conn_lock2(struct ncp_conn
*conn
, struct thread
*td
,
148 struct ucred
*cred
, int mode
)
152 error
= ncp_conn_access(conn
, cred
, mode
);
154 ncp_conn_unlocklist(td
);
158 ncp_conn_unlocklist(td
);
159 error
= ncp_conn_lock_any(conn
, td
, cred
);
161 if (conn
->nc_lwant
== 0) {
162 wakeup(&conn
->nc_lwant
);
168 ncp_conn_unlock(struct ncp_conn
*conn
, struct thread
*td
) {
170 * note, that LK_RELASE will do wakeup() instead of wakeup_one().
171 * this will do a little overhead
173 lockmgr(&conn
->nc_lock
, LK_RELEASE
, NULL
, td
);
177 ncp_conn_assert_locked(struct ncp_conn
*conn
, const char *checker
,
180 if (conn
->nc_lock
.lk_flags
& LK_HAVE_EXCL
)
182 printf("%s: connection isn't locked!\n", checker
);
187 * create, fill with defaults and return in locked state
190 ncp_conn_alloc(struct thread
*td
, struct ucred
*cred
, struct ncp_conn
**conn
)
193 struct ncp_conn
*ncp
;
195 MALLOC(ncp
, struct ncp_conn
*, sizeof(struct ncp_conn
),
196 M_NCPDATA
, M_WAITOK
| M_ZERO
);
198 lockinit(&ncp
->nc_lock
, "ncplck", 0, 0);
200 ncp
->nc_id
= ncp_next_ref
++;
201 ncp
->nc_owner
= cred
;
203 ncp
->connid
= 0xFFFF;
204 ncp_conn_lock_any(ncp
, td
, ncp
->nc_owner
);
206 ncp_conn_locklist(LK_EXCLUSIVE
, td
);
207 SLIST_INSERT_HEAD(&conn_list
,ncp
,nc_next
);
208 ncp_conn_unlocklist(td
);
213 * Remove the connection, on entry it must be locked
216 ncp_conn_free(struct ncp_conn
*ncp
)
219 struct ncp_conn
*ncp1
;
221 if (ncp
->nc_id
== 0) {
222 printf("already!!!!\n");
226 NCPFATAL("conn==NULL !\n");
229 error
= ncp_conn_assert_locked(ncp
, __func__
, ncp
->td
);
230 if (error
) return error
;
232 NCPFATAL("there are %d referenses left\n",ncp
->ref_cnt
);
236 * Mark conn as died and wait for other process
239 ncp_conn_unlock(ncp
, ncp
->td
);
241 * if signal is raised - how I do react ?
243 lockmgr(&ncp
->nc_lock
, LK_DRAIN
, NULL
, ncp
->td
);
244 while (ncp
->nc_lwant
) {
245 printf("lwant = %d\n", ncp
->nc_lwant
);
246 tsleep(&ncp
->nc_lwant
, 0, "ncpdr", 2*hz
);
248 ncp_conn_locklist(LK_EXCLUSIVE
, ncp
->td
);
250 * It is possible, that other process destroy connection while we draining,
251 * and free it. So, we must rescan list
253 SLIST_FOREACH(ncp1
, &conn_list
, nc_next
) {
254 if (ncp1
== ncp
) break;
257 ncp_conn_unlocklist(ncp
->td
);
260 SLIST_REMOVE(&conn_list
, ncp
, ncp_conn
, nc_next
);
262 ncp_conn_unlocklist(ncp
->td
);
263 if (ncp
->li
.user
) free(ncp
->li
.user
, M_NCPDATA
);
264 if (ncp
->li
.password
) free(ncp
->li
.password
, M_NCPDATA
);
265 crfree(ncp
->nc_owner
);
266 FREE(ncp
, M_NCPDATA
);
271 * Lookup connection by handle, return a locked conn descriptor
274 ncp_conn_getbyref(int ref
,struct thread
*td
,struct ucred
*cred
, int mode
, struct ncp_conn
**connpp
){
275 struct ncp_conn
*ncp
;
278 ncp_conn_locklist(LK_SHARED
, td
);
279 SLIST_FOREACH(ncp
, &conn_list
, nc_next
)
280 if (ncp
->nc_id
== ref
) break;
282 ncp_conn_unlocklist(td
);
285 error
= ncp_conn_lock2(ncp
, td
, cred
, mode
);
291 * find attached, but not logged in connection to specified server
294 ncp_conn_getattached(struct ncp_conn_args
*li
,struct thread
*td
,struct ucred
*cred
,int mode
, struct ncp_conn
**connpp
){
295 struct ncp_conn
*ncp
, *ncp2
=NULL
;
298 ncp_conn_locklist(LK_SHARED
, td
);
299 SLIST_FOREACH(ncp
, &conn_list
, nc_next
) {
300 if ((ncp
->flags
& NCPFL_LOGGED
) != 0 ||
301 strcmp(ncp
->li
.server
,li
->server
) != 0 ||
302 ncp
->li
.saddr
.sa_len
!= li
->saddr
.sa_len
||
303 bcmp(&ncp
->li
.saddr
,&ncp
->li
.saddr
,li
->saddr
.sa_len
) != 0)
305 if (ncp_suser(cred
) == 0 ||
306 cred
->cr_uid
== ncp
->nc_owner
->cr_uid
)
308 error
= ncp_conn_access(ncp
,cred
,mode
);
309 if (!error
&& ncp2
== NULL
)
312 if (ncp
== NULL
) ncp
= ncp2
;
314 ncp_conn_unlocklist(td
);
317 error
= ncp_conn_lock2(ncp
,td
,cred
,mode
);
324 * Lookup connection by server/user pair, return a locked conn descriptor.
325 * if li is NULL or server/user pair incomplete, try to select best connection
327 * Connection selected in next order:
328 * 1. Try to search conn with ucred owner, if li is NULL also find a primary
329 * 2. If 1. fails try to get first suitable shared connection
330 * 3. If 2. fails then nothing can help to poor ucred owner
334 ncp_conn_getbyli(struct ncp_conn_args
*li
, struct thread
*td
,
335 struct ucred
*cred
, int mode
, struct ncp_conn
**connpp
)
337 struct ncp_conn
*ncp
, *ncp2
=NULL
;
338 int error
=0, partial
, haveserv
;
340 partial
= (li
== NULL
|| li
->server
[0] == 0 || li
->user
== NULL
);
341 haveserv
= (li
&& li
->server
[0]);
342 ncp_conn_locklist(LK_SHARED
, td
);
343 SLIST_FOREACH(ncp
, &conn_list
, nc_next
) {
345 if (cred
->cr_uid
== ncp
->nc_owner
->cr_uid
) {
347 if (strcmp(ncp
->li
.server
,li
->server
) == 0)
350 if (ncp
->flags
& NCPFL_PRIMARY
)
357 if (strcmp(ncp
->li
.server
,li
->server
) != 0 ||
358 ncp
->li
.user
== NULL
||
359 strcmp(ncp
->li
.user
,li
->user
) != 0)
361 if (cred
->cr_uid
== ncp
->nc_owner
->cr_uid
)
363 if (ncp_suser(cred
) == 0)
366 error
= ncp_conn_access(ncp
,cred
,mode
);
367 if (!error
&& ncp2
== NULL
)
370 if (ncp
== NULL
) ncp
= ncp2
;
372 ncp_conn_unlocklist(td
);
375 error
= ncp_conn_lock2(ncp
,td
,cred
,mode
);
382 * Set primary connection flag, since it have sence only for an owner,
383 * only owner can modify this flag.
384 * connection expected to be locked.
387 ncp_conn_setprimary(struct ncp_conn
*conn
, int on
){
388 struct ncp_conn
*ncp
=NULL
;
390 if (conn
->ucred
->cr_uid
!= conn
->nc_owner
->cr_uid
)
392 ncp_conn_locklist(LK_SHARED
, conn
->td
);
393 SLIST_FOREACH(ncp
, &conn_list
, nc_next
) {
394 if (conn
->ucred
->cr_uid
== ncp
->nc_owner
->cr_uid
)
395 ncp
->flags
&= ~NCPFL_PRIMARY
;
397 ncp_conn_unlocklist(conn
->td
);
399 conn
->flags
|= NCPFL_PRIMARY
;
403 * Lease conn to given proc, returning unique handle
404 * problem: how locks should be applied ?
407 ncp_conn_gethandle(struct ncp_conn
*conn
, struct thread
*td
, struct ncp_handle
**handle
){
408 struct ncp_handle
*refp
;
410 lockmgr(&lhlock
, LK_EXCLUSIVE
, NULL
, td
);
411 SLIST_FOREACH(refp
, &lhlist
, nh_next
)
412 if (refp
->nh_conn
== conn
&& td
== refp
->nh_td
) break;
417 lockmgr(&lhlock
, LK_RELEASE
, NULL
, td
);
420 MALLOC(refp
,struct ncp_handle
*,sizeof(struct ncp_handle
),M_NCPDATA
,
422 SLIST_INSERT_HEAD(&lhlist
,refp
,nh_next
);
425 refp
->nh_conn
= conn
;
426 refp
->nh_id
= ncp_next_handle
++;
429 lockmgr(&lhlock
, LK_RELEASE
, NULL
, td
);
433 * release reference, if force - ignore refcount
436 ncp_conn_puthandle(struct ncp_handle
*handle
, struct thread
*td
, int force
) {
437 struct ncp_handle
*refp
= handle
;
439 lockmgr(&lhlock
, LK_EXCLUSIVE
, NULL
, td
);
441 refp
->nh_conn
->ref_cnt
--;
443 refp
->nh_conn
->ref_cnt
-= refp
->nh_ref
;
446 if (refp
->nh_ref
== 0) {
447 SLIST_REMOVE(&lhlist
, refp
, ncp_handle
, nh_next
);
448 FREE(refp
, M_NCPDATA
);
450 lockmgr(&lhlock
, LK_RELEASE
, NULL
, td
);
457 ncp_conn_findhandle(int connHandle
, struct thread
*td
, struct ncp_handle
**handle
) {
458 struct ncp_handle
*refp
;
460 lockmgr(&lhlock
, LK_SHARED
, NULL
, td
);
461 SLIST_FOREACH(refp
, &lhlist
, nh_next
)
462 if (refp
->nh_td
== td
&& refp
->nh_id
== connHandle
) break;
463 lockmgr(&lhlock
, LK_RELEASE
, NULL
, td
);
471 * Clear handles associated with specified process
474 ncp_conn_putprochandles(struct thread
*td
) {
475 struct ncp_handle
*hp
, *nhp
;
478 lockmgr(&lhlock
, LK_EXCLUSIVE
, NULL
, td
);
479 for (hp
= SLIST_FIRST(&lhlist
); hp
; hp
= nhp
) {
480 nhp
= SLIST_NEXT(hp
, nh_next
);
481 if (hp
->nh_td
!= td
) continue;
483 hp
->nh_conn
->ref_cnt
-= hp
->nh_ref
;
484 SLIST_REMOVE(&lhlist
, hp
, ncp_handle
, nh_next
);
487 lockmgr(&lhlock
, LK_RELEASE
, NULL
, td
);
491 * remove references in all possible connections,
492 * XXX - possible problem is a locked list.
495 ncp_conn_list_rm_ref(pid_t pid) {
496 struct ncp_conn *ncp;
498 ncp_conn_locklist(LK_SHARED, NULL);
499 SLIST_FOREACH(ncp, &conn_list, nc_next) {
500 ncp_conn_rm_ref(ncp,pid,1);
502 ncp_conn_unlocklist(NULL);
507 ncp_conn_getinfo(struct ncp_conn
*ncp
, struct ncp_conn_stat
*ncs
) {
508 bzero(ncs
,sizeof(*ncs
));
510 ncs
->li
.user
= ncs
->user
;
512 strcpy(ncs
->user
, ncp
->li
.user
);
513 ncs
->li
.password
= NULL
;
514 ncs
->connRef
= ncp
->nc_id
;
515 ncs
->ref_cnt
= ncp
->ref_cnt
;
516 ncs
->connid
= ncp
->connid
;
517 ncs
->owner
= ncp
->nc_owner
->cr_uid
;
518 ncs
->group
= ncp
->nc_group
;
519 ncs
->flags
= ncp
->flags
;
520 ncs
->buffer_size
= ncp
->buffer_size
;
525 ncp_sysctl_connstat(SYSCTL_HANDLER_ARGS
) {
527 struct ncp_conn_stat ncs
;
528 struct ncp_conn
*ncp
;
529 /* struct ucred *cred = req->p->p_ucred;*/
532 ncp_conn_locklist(LK_SHARED
, req
->td
);
533 error
= SYSCTL_OUT(req
, &ncp_conn_cnt
, sizeof(ncp_conn_cnt
));
534 SLIST_FOREACH(ncp
, &conn_list
, nc_next
) {
536 /* I can't do conn_lock while list is locked */
539 ncp_conn_getinfo(ncp
, &ncs
);
541 bzero(&ncs
,sizeof(ncs
));
542 ncs
.connRef
= ncp
->nc_id
;
543 strcpy(ncs
.li
.server
,"***");
546 error
= SYSCTL_OUT(req
, &ncs
, sizeof(ncs
));
548 ncp_conn_unlocklist(req
->td
);