xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_session.c (revision faa1795a28a5c712eed6d0a3f84d98c368a316c6)
1da6c28aaSamw /*
2da6c28aaSamw  * CDDL HEADER START
3da6c28aaSamw  *
4da6c28aaSamw  * The contents of this file are subject to the terms of the
5da6c28aaSamw  * Common Development and Distribution License (the "License").
6da6c28aaSamw  * You may not use this file except in compliance with the License.
7da6c28aaSamw  *
8da6c28aaSamw  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9da6c28aaSamw  * or http://www.opensolaris.org/os/licensing.
10da6c28aaSamw  * See the License for the specific language governing permissions
11da6c28aaSamw  * and limitations under the License.
12da6c28aaSamw  *
13da6c28aaSamw  * When distributing Covered Code, include this CDDL HEADER in each
14da6c28aaSamw  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15da6c28aaSamw  * If applicable, add the following below this CDDL HEADER, with the
16da6c28aaSamw  * fields enclosed by brackets "[]" replaced with your own identifying
17da6c28aaSamw  * information: Portions Copyright [yyyy] [name of copyright owner]
18da6c28aaSamw  *
19da6c28aaSamw  * CDDL HEADER END
20da6c28aaSamw  */
21da6c28aaSamw /*
225cdbe942Sjb  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23da6c28aaSamw  * Use is subject to license terms.
24da6c28aaSamw  */
25da6c28aaSamw 
26da6c28aaSamw #pragma ident	"%Z%%M%	%I%	%E% SMI"
27da6c28aaSamw 
28da6c28aaSamw #include <sys/atomic.h>
29da6c28aaSamw #include <sys/strsubr.h>
30da6c28aaSamw #include <sys/synch.h>
31da6c28aaSamw #include <sys/types.h>
32da6c28aaSamw #include <sys/socketvar.h>
33da6c28aaSamw #include <sys/sdt.h>
34da6c28aaSamw #include <smbsrv/netbios.h>
35da6c28aaSamw #include <smbsrv/smb_incl.h>
36da6c28aaSamw #include <smbsrv/smb_i18n.h>
37da6c28aaSamw 
38*faa1795aSjb static volatile uint64_t smb_kids;
39da6c28aaSamw 
40*faa1795aSjb uint32_t smb_keep_alive = SSN_KEEP_ALIVE_TIMEOUT;
41da6c28aaSamw 
42da6c28aaSamw static int smb_session_message(smb_session_t *);
43da6c28aaSamw static int smb_session_xprt_puthdr(smb_session_t *, smb_xprt_t *,
44da6c28aaSamw     uint8_t *, size_t);
45da6c28aaSamw 
46*faa1795aSjb static void smb_request_init_command_mbuf(smb_request_t *sr);
47da6c28aaSamw 
48da6c28aaSamw 
49da6c28aaSamw void
50*faa1795aSjb smb_session_timers(smb_session_list_t *se)
51da6c28aaSamw {
52*faa1795aSjb 	smb_session_t	*session;
53da6c28aaSamw 
54*faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
55*faa1795aSjb 	session = list_head(&se->se_act.lst);
56*faa1795aSjb 	while (session) {
57da6c28aaSamw 		/*
58da6c28aaSamw 		 * Walk through the table and decrement each keep_alive
59da6c28aaSamw 		 * timer that has not timed out yet. (keepalive > 0)
60da6c28aaSamw 		 */
61*faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
62*faa1795aSjb 		if (session->keep_alive &&
63*faa1795aSjb 		    (session->keep_alive != (uint32_t)-1))
64*faa1795aSjb 			session->keep_alive--;
65*faa1795aSjb 		session = list_next(&se->se_act.lst, session);
66*faa1795aSjb 	}
67*faa1795aSjb 	rw_exit(&se->se_lock);
68*faa1795aSjb }
69da6c28aaSamw 
70*faa1795aSjb void
71*faa1795aSjb smb_session_correct_keep_alive_values(
72*faa1795aSjb     smb_session_list_t	*se,
73*faa1795aSjb     uint32_t		new_keep_alive)
74*faa1795aSjb {
75*faa1795aSjb 	smb_session_t		*sn;
76*faa1795aSjb 
77*faa1795aSjb 	if (new_keep_alive == smb_keep_alive)
78*faa1795aSjb 		return;
79*faa1795aSjb 	/*
80*faa1795aSjb 	 * keep alive == 0 means do not drop connection if it's idle
81*faa1795aSjb 	 */
82*faa1795aSjb 	smb_keep_alive = (new_keep_alive) ? new_keep_alive : -1;
83*faa1795aSjb 
84*faa1795aSjb 	/*
85*faa1795aSjb 	 * Walk through the table and set each session to the new keep_alive
86*faa1795aSjb 	 * value if they have not already timed out.  Block clock interrupts.
87*faa1795aSjb 	 */
88*faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
89*faa1795aSjb 	sn = list_head(&se->se_rdy.lst);
90*faa1795aSjb 	while (sn) {
91*faa1795aSjb 		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
92*faa1795aSjb 		sn->keep_alive = new_keep_alive;
93*faa1795aSjb 		sn = list_next(&se->se_rdy.lst, sn);
94da6c28aaSamw 	}
95*faa1795aSjb 	sn = list_head(&se->se_act.lst);
96*faa1795aSjb 	while (sn) {
97*faa1795aSjb 		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
98*faa1795aSjb 		if (sn->keep_alive)
99*faa1795aSjb 			sn->keep_alive = new_keep_alive;
100*faa1795aSjb 		sn = list_next(&se->se_act.lst, sn);
101*faa1795aSjb 	}
102*faa1795aSjb 	rw_exit(&se->se_lock);
103da6c28aaSamw }
104da6c28aaSamw 
105da6c28aaSamw /*
106da6c28aaSamw  * smb_reconnection_check
107da6c28aaSamw  *
108da6c28aaSamw  * This function is called when a client indicates its current connection
109da6c28aaSamw  * should be the only one it has with the server, as indicated by VC=0 in
110da6c28aaSamw  * a SessionSetupX request. We go through the session list and destroy any
111da6c28aaSamw  * stale connections for that client.
112da6c28aaSamw  *
113da6c28aaSamw  * Clients don't associate IP addresses and servers. So a client may make
114da6c28aaSamw  * independent connections (i.e. with VC=0) to a server with multiple
115da6c28aaSamw  * IP addresses. So, when checking for a reconnection, we need to include
116da6c28aaSamw  * the local IP address, to which the client is connecting, when checking
117da6c28aaSamw  * for stale sessions.
118da6c28aaSamw  *
119da6c28aaSamw  * Also check the server's NetBIOS name to support simultaneous access by
120da6c28aaSamw  * multiple clients behind a NAT server.  This will only work for SMB over
121da6c28aaSamw  * NetBIOS on TCP port 139, it will not work SMB over TCP port 445 because
122da6c28aaSamw  * there is no NetBIOS name.  See also Knowledge Base article Q301673.
123da6c28aaSamw  */
124da6c28aaSamw void
125*faa1795aSjb smb_session_reconnection_check(smb_session_list_t *se, smb_session_t *session)
126da6c28aaSamw {
127*faa1795aSjb 	smb_session_t	*sn;
128da6c28aaSamw 
129*faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
130*faa1795aSjb 	sn = list_head(&se->se_act.lst);
131*faa1795aSjb 	while (sn) {
132da6c28aaSamw 		ASSERT(sn->s_magic == SMB_SESSION_MAGIC);
133da6c28aaSamw 		if ((sn != session) &&
134da6c28aaSamw 		    (sn->ipaddr == session->ipaddr) &&
135da6c28aaSamw 		    (sn->local_ipaddr == session->local_ipaddr) &&
136da6c28aaSamw 		    (strcasecmp(sn->workstation, session->workstation) == 0) &&
137da6c28aaSamw 		    (sn->opentime <= session->opentime) &&
138da6c28aaSamw 		    (sn->s_kid < session->s_kid)) {
139*faa1795aSjb 			tsignal(sn->s_thread, SIGINT);
140da6c28aaSamw 		}
141*faa1795aSjb 		sn = list_next(&se->se_act.lst, sn);
142da6c28aaSamw 	}
143*faa1795aSjb 	rw_exit(&se->se_lock);
144da6c28aaSamw }
145da6c28aaSamw 
146da6c28aaSamw /*
147da6c28aaSamw  * Send a session message - supports SMB-over-NBT and SMB-over-TCP.
148da6c28aaSamw  *
149da6c28aaSamw  * The mbuf chain is copied into a contiguous buffer so that the whole
150da6c28aaSamw  * message is submitted to smb_sosend as a single request.  This should
151da6c28aaSamw  * help Ethereal/Wireshark delineate the packets correctly even though
152da6c28aaSamw  * TCP_NODELAY has been set on the socket.
153da6c28aaSamw  *
154da6c28aaSamw  * If an mbuf chain is provided, it will be freed and set to NULL here.
155da6c28aaSamw  */
156da6c28aaSamw int
157da6c28aaSamw smb_session_send(smb_session_t *session, uint8_t type, struct mbuf_chain *mbc)
158da6c28aaSamw {
1595cdbe942Sjb 	struct mbuf	*m = NULL;
1605cdbe942Sjb 	smb_txbuf_t	*txb;
1615cdbe942Sjb 	int		len = 0;
1625cdbe942Sjb 	smb_xprt_t	hdr;
1635cdbe942Sjb 	int		rc;
1645cdbe942Sjb 	uint8_t		*data;
165da6c28aaSamw 
166da6c28aaSamw 	switch (session->s_state) {
167da6c28aaSamw 	case SMB_SESSION_STATE_DISCONNECTED:
168da6c28aaSamw 	case SMB_SESSION_STATE_TERMINATED:
169da6c28aaSamw 		if ((mbc != NULL) && (mbc->chain != NULL)) {
170da6c28aaSamw 			m_freem(mbc->chain);
171da6c28aaSamw 			mbc->chain = NULL;
172da6c28aaSamw 			mbc->flags = 0;
173da6c28aaSamw 		}
174da6c28aaSamw 		return (ENOTCONN);
175da6c28aaSamw 	default:
176da6c28aaSamw 		break;
177da6c28aaSamw 	}
178da6c28aaSamw 
1795cdbe942Sjb 	txb = smb_net_txb_alloc();
180da6c28aaSamw 
181da6c28aaSamw 	if ((mbc != NULL) && (mbc->chain != NULL)) {
1825cdbe942Sjb 		len = NETBIOS_HDR_SZ;	/* Account for the NBT header. */
183da6c28aaSamw 		m = mbc->chain;
1845cdbe942Sjb 		data = &txb->tb_data[len];
185da6c28aaSamw 
186da6c28aaSamw 		while (m) {
1875cdbe942Sjb 			if ((len + m->m_len) > sizeof (txb->tb_data)) {
1885cdbe942Sjb 				smb_net_txb_free(txb);
189da6c28aaSamw 				m_freem(mbc->chain);
190da6c28aaSamw 				mbc->chain = NULL;
191da6c28aaSamw 				mbc->flags = 0;
192da6c28aaSamw 				return (EMSGSIZE);
193da6c28aaSamw 			}
1945cdbe942Sjb 			bcopy(m->m_data, data, m->m_len);
1955cdbe942Sjb 			data += m->m_len;
1965cdbe942Sjb 			len += m->m_len;
197da6c28aaSamw 			m = m->m_next;
198da6c28aaSamw 		}
199da6c28aaSamw 
200da6c28aaSamw 		m_freem(mbc->chain);
201da6c28aaSamw 		mbc->chain = NULL;
202da6c28aaSamw 		mbc->flags = 0;
2035cdbe942Sjb 		len -= NETBIOS_HDR_SZ;
204da6c28aaSamw 	}
205da6c28aaSamw 
206da6c28aaSamw 	hdr.xh_type = type;
2075cdbe942Sjb 	hdr.xh_length = len;
208da6c28aaSamw 
2095cdbe942Sjb 	rc = smb_session_xprt_puthdr(session, &hdr, txb->tb_data,
2105cdbe942Sjb 	    NETBIOS_HDR_SZ);
211da6c28aaSamw 	if (rc == 0) {
2125cdbe942Sjb 		txb->tb_len = len + NETBIOS_HDR_SZ;
2135cdbe942Sjb 		rc = smb_net_txb_send(session->sock, &session->s_txlst, txb);
2145cdbe942Sjb 	} else {
2155cdbe942Sjb 		smb_net_txb_free(txb);
216da6c28aaSamw 	}
217da6c28aaSamw 	return (rc);
218da6c28aaSamw }
219da6c28aaSamw 
220da6c28aaSamw /*
221da6c28aaSamw  * Read, process and respond to a NetBIOS session request.
222da6c28aaSamw  *
223da6c28aaSamw  * A NetBIOS session must be established for SMB-over-NetBIOS.  Validate
224da6c28aaSamw  * the calling and called name format and save the client NetBIOS name,
225da6c28aaSamw  * which is used when a NetBIOS session is established to check for and
226da6c28aaSamw  * cleanup leftover state from a previous session.
227da6c28aaSamw  *
228da6c28aaSamw  * Session requests are not valid for SMB-over-TCP, which is unfortunate
229da6c28aaSamw  * because without the client name leftover state cannot be cleaned up
230da6c28aaSamw  * if the client is behind a NAT server.
231da6c28aaSamw  */
232da6c28aaSamw static int
233da6c28aaSamw smb_session_request(struct smb_session *session)
234da6c28aaSamw {
235da6c28aaSamw 	int			rc;
236da6c28aaSamw 	char			*calling_name;
237da6c28aaSamw 	char			*called_name;
238da6c28aaSamw 	char 			client_name[NETBIOS_NAME_SZ];
239da6c28aaSamw 	struct mbuf_chain 	mbc;
240da6c28aaSamw 	char 			*names = NULL;
241da6c28aaSamw 	mts_wchar_t		*wbuf = NULL;
242da6c28aaSamw 	smb_xprt_t		hdr;
243da6c28aaSamw 	char *p;
244da6c28aaSamw 	unsigned int cpid = oem_get_smb_cpid();
245da6c28aaSamw 	int rc1, rc2;
246da6c28aaSamw 
247da6c28aaSamw 	session->keep_alive = smb_keep_alive;
248da6c28aaSamw 
249*faa1795aSjb 	if ((rc = smb_session_xprt_gethdr(session, &hdr)) != 0)
250*faa1795aSjb 		return (rc);
251da6c28aaSamw 
252da6c28aaSamw 	DTRACE_PROBE2(receive__session__req__xprthdr, struct session *, session,
253da6c28aaSamw 	    smb_xprt_t *, &hdr);
254da6c28aaSamw 
255da6c28aaSamw 	if ((hdr.xh_type != SESSION_REQUEST) ||
256da6c28aaSamw 	    (hdr.xh_length != NETBIOS_SESSION_REQUEST_DATA_LENGTH)) {
257da6c28aaSamw 		DTRACE_PROBE1(receive__session__req__failed,
258da6c28aaSamw 		    struct session *, session);
259da6c28aaSamw 		return (EINVAL);
260da6c28aaSamw 	}
261da6c28aaSamw 
262da6c28aaSamw 	names = kmem_alloc(hdr.xh_length, KM_SLEEP);
263da6c28aaSamw 
264da6c28aaSamw 	if ((rc = smb_sorecv(session->sock, names, hdr.xh_length)) != 0) {
265da6c28aaSamw 		kmem_free(names, hdr.xh_length);
266da6c28aaSamw 		DTRACE_PROBE1(receive__session__req__failed,
267da6c28aaSamw 		    struct session *, session);
268da6c28aaSamw 		return (rc);
269da6c28aaSamw 	}
270da6c28aaSamw 
271da6c28aaSamw 	DTRACE_PROBE3(receive__session__req__data, struct session *, session,
272da6c28aaSamw 	    char *, names, uint32_t, hdr.xh_length);
273da6c28aaSamw 
274da6c28aaSamw 	called_name = &names[0];
275da6c28aaSamw 	calling_name = &names[NETBIOS_ENCODED_NAME_SZ + 2];
276da6c28aaSamw 
277da6c28aaSamw 	rc1 = netbios_name_isvalid(called_name, 0);
278da6c28aaSamw 	rc2 = netbios_name_isvalid(calling_name, client_name);
279da6c28aaSamw 
280da6c28aaSamw 	if (rc1 == 0 || rc2 == 0) {
281da6c28aaSamw 
282da6c28aaSamw 		DTRACE_PROBE3(receive__invalid__session__req,
283da6c28aaSamw 		    struct session *, session, char *, names,
284da6c28aaSamw 		    uint32_t, hdr.xh_length);
285da6c28aaSamw 
286da6c28aaSamw 		kmem_free(names, hdr.xh_length);
287da6c28aaSamw 		MBC_INIT(&mbc, MAX_DATAGRAM_LENGTH);
288da6c28aaSamw 		(void) smb_encode_mbc(&mbc, "b",
289da6c28aaSamw 		    DATAGRAM_INVALID_SOURCE_NAME_FORMAT);
290da6c28aaSamw 		(void) smb_session_send(session, NEGATIVE_SESSION_RESPONSE,
291da6c28aaSamw 		    &mbc);
292da6c28aaSamw 		return (EINVAL);
293da6c28aaSamw 	}
294da6c28aaSamw 
295da6c28aaSamw 	DTRACE_PROBE3(receive__session__req__calling__decoded,
296da6c28aaSamw 	    struct session *, session,
297da6c28aaSamw 	    char *, calling_name, char *, client_name);
298da6c28aaSamw 
299da6c28aaSamw 	/*
300da6c28aaSamw 	 * The client NetBIOS name is in oem codepage format.
301da6c28aaSamw 	 * We need to convert it to unicode and store it in
302da6c28aaSamw 	 * multi-byte format.  We also need to strip off any
303da6c28aaSamw 	 * spaces added as part of the NetBIOS name encoding.
304da6c28aaSamw 	 */
305da6c28aaSamw 	wbuf = kmem_alloc((SMB_PI_MAX_HOST * sizeof (mts_wchar_t)), KM_SLEEP);
306da6c28aaSamw 	(void) oemstounicodes(wbuf, client_name, SMB_PI_MAX_HOST, cpid);
307da6c28aaSamw 	(void) mts_wcstombs(session->workstation, wbuf, SMB_PI_MAX_HOST);
308da6c28aaSamw 	kmem_free(wbuf, (SMB_PI_MAX_HOST * sizeof (mts_wchar_t)));
309da6c28aaSamw 
310da6c28aaSamw 	if ((p = strchr(session->workstation, ' ')) != 0)
311da6c28aaSamw 		*p = '\0';
312da6c28aaSamw 
313da6c28aaSamw 	kmem_free(names, hdr.xh_length);
314da6c28aaSamw 	return (smb_session_send(session, POSITIVE_SESSION_RESPONSE, NULL));
315da6c28aaSamw }
316da6c28aaSamw 
317da6c28aaSamw /*
318da6c28aaSamw  * Read 4-byte header from the session socket and build an in-memory
319da6c28aaSamw  * session transport header.  See smb_xprt_t definition for header
320da6c28aaSamw  * format information.
321da6c28aaSamw  *
322da6c28aaSamw  * Direct hosted NetBIOS-less SMB (SMB-over-TCP) uses port 445.  The
323da6c28aaSamw  * first byte of the four-byte header must be 0 and the next three
324da6c28aaSamw  * bytes contain the length of the remaining data.
325da6c28aaSamw  */
326da6c28aaSamw int
327da6c28aaSamw smb_session_xprt_gethdr(smb_session_t *session, smb_xprt_t *ret_hdr)
328da6c28aaSamw {
329*faa1795aSjb 	int		rc;
330*faa1795aSjb 	unsigned char	buf[NETBIOS_HDR_SZ];
331da6c28aaSamw 
332*faa1795aSjb 	if ((rc = smb_sorecv(session->sock, buf, NETBIOS_HDR_SZ)) != 0)
333*faa1795aSjb 		return (rc);
334da6c28aaSamw 
335da6c28aaSamw 	switch (session->s_local_port) {
336da6c28aaSamw 	case SSN_SRVC_TCP_PORT:
337da6c28aaSamw 		ret_hdr->xh_type = buf[0];
338da6c28aaSamw 		ret_hdr->xh_length = (((uint32_t)buf[1] & 1) << 16) |
339da6c28aaSamw 		    ((uint32_t)buf[2] << 8) |
340da6c28aaSamw 		    ((uint32_t)buf[3]);
341da6c28aaSamw 		break;
342da6c28aaSamw 
343da6c28aaSamw 	case SMB_SRVC_TCP_PORT:
344da6c28aaSamw 		ret_hdr->xh_type = buf[0];
345da6c28aaSamw 
346da6c28aaSamw 		if (ret_hdr->xh_type != 0) {
347da6c28aaSamw 			cmn_err(CE_WARN, "0x%08x: invalid type (%u)",
348da6c28aaSamw 			    session->ipaddr, ret_hdr->xh_type);
349*faa1795aSjb 			return (EPROTO);
350da6c28aaSamw 		}
351da6c28aaSamw 
352da6c28aaSamw 		ret_hdr->xh_length = ((uint32_t)buf[1] << 16) |
353da6c28aaSamw 		    ((uint32_t)buf[2] << 8) |
354da6c28aaSamw 		    ((uint32_t)buf[3]);
355da6c28aaSamw 		break;
356da6c28aaSamw 
357da6c28aaSamw 	default:
358da6c28aaSamw 		cmn_err(CE_WARN, "0x%08x: invalid port %u",
359da6c28aaSamw 		    session->ipaddr, session->s_local_port);
360*faa1795aSjb 		return (EPROTO);
361da6c28aaSamw 	}
362da6c28aaSamw 
363da6c28aaSamw 	return (0);
364da6c28aaSamw }
365da6c28aaSamw 
366da6c28aaSamw /*
367da6c28aaSamw  * Encode a transport session packet header into a 4-byte buffer.
368da6c28aaSamw  * See smb_xprt_t definition for header format information.
369da6c28aaSamw  */
370da6c28aaSamw static int
371da6c28aaSamw smb_session_xprt_puthdr(smb_session_t *session, smb_xprt_t *hdr,
372da6c28aaSamw     uint8_t *buf, size_t buflen)
373da6c28aaSamw {
374da6c28aaSamw 	if (session == NULL || hdr == NULL ||
375da6c28aaSamw 	    buf == NULL || buflen < NETBIOS_HDR_SZ) {
376da6c28aaSamw 		return (-1);
377da6c28aaSamw 	}
378da6c28aaSamw 
379da6c28aaSamw 	switch (session->s_local_port) {
380da6c28aaSamw 	case SSN_SRVC_TCP_PORT:
381da6c28aaSamw 		buf[0] = hdr->xh_type;
382da6c28aaSamw 		buf[1] = ((hdr->xh_length >> 16) & 1);
383da6c28aaSamw 		buf[2] = (hdr->xh_length >> 8) & 0xff;
384da6c28aaSamw 		buf[3] = hdr->xh_length & 0xff;
385da6c28aaSamw 		break;
386da6c28aaSamw 
387da6c28aaSamw 	case SMB_SRVC_TCP_PORT:
388da6c28aaSamw 		buf[0] = hdr->xh_type;
389da6c28aaSamw 		buf[1] = (hdr->xh_length >> 16) & 0xff;
390da6c28aaSamw 		buf[2] = (hdr->xh_length >> 8) & 0xff;
391da6c28aaSamw 		buf[3] = hdr->xh_length & 0xff;
392da6c28aaSamw 		break;
393da6c28aaSamw 
394da6c28aaSamw 	default:
395da6c28aaSamw 		cmn_err(CE_WARN, "0x%08x: invalid port (%u)",
396da6c28aaSamw 		    session->ipaddr, session->s_local_port);
397da6c28aaSamw 		return (-1);
398da6c28aaSamw 	}
399da6c28aaSamw 
400da6c28aaSamw 	return (0);
401da6c28aaSamw }
402da6c28aaSamw 
403*faa1795aSjb static void
404da6c28aaSamw smb_request_init_command_mbuf(smb_request_t *sr)
405da6c28aaSamw {
406da6c28aaSamw 	MGET(sr->command.chain, 0, MT_DATA);
407da6c28aaSamw 
408da6c28aaSamw 	/*
409da6c28aaSamw 	 * Setup mbuf, mimic MCLGET but use the complete packet buffer.
410da6c28aaSamw 	 */
411da6c28aaSamw 	sr->command.chain->m_ext.ext_buf = sr->sr_request_buf;
412da6c28aaSamw 	sr->command.chain->m_data = sr->command.chain->m_ext.ext_buf;
413da6c28aaSamw 	sr->command.chain->m_len = sr->sr_req_length;
414da6c28aaSamw 	sr->command.chain->m_flags |= M_EXT;
415da6c28aaSamw 	sr->command.chain->m_ext.ext_size = sr->sr_req_length;
416da6c28aaSamw 	sr->command.chain->m_ext.ext_ref = &mclrefnoop;
417da6c28aaSamw 
418da6c28aaSamw 	/*
419da6c28aaSamw 	 * Initialize the rest of the mbuf_chain fields
420da6c28aaSamw 	 */
421da6c28aaSamw 	sr->command.flags = 0;
422da6c28aaSamw 	sr->command.shadow_of = 0;
423da6c28aaSamw 	sr->command.max_bytes = sr->sr_req_length;
424da6c28aaSamw 	sr->command.chain_offset = 0;
425da6c28aaSamw }
426da6c28aaSamw 
427da6c28aaSamw /*
428da6c28aaSamw  * smb_request_cancel
429da6c28aaSamw  *
430da6c28aaSamw  * Handle a cancel for a request properly depending on the current request
431da6c28aaSamw  * state.
432da6c28aaSamw  */
433da6c28aaSamw void
434da6c28aaSamw smb_request_cancel(smb_request_t *sr)
435da6c28aaSamw {
436da6c28aaSamw 	mutex_enter(&sr->sr_mutex);
437da6c28aaSamw 	switch (sr->sr_state) {
438da6c28aaSamw 
439da6c28aaSamw 	case SMB_REQ_STATE_SUBMITTED:
440da6c28aaSamw 	case SMB_REQ_STATE_ACTIVE:
441da6c28aaSamw 	case SMB_REQ_STATE_CLEANED_UP:
442da6c28aaSamw 		sr->sr_state = SMB_REQ_STATE_CANCELED;
443da6c28aaSamw 		break;
444da6c28aaSamw 
445da6c28aaSamw 	case SMB_REQ_STATE_WAITING_LOCK:
446da6c28aaSamw 		/*
447da6c28aaSamw 		 * This request is waiting on a lock.  Wakeup everything
448da6c28aaSamw 		 * waiting on the lock so that the relevant thread regains
449da6c28aaSamw 		 * control and notices that is has been canceled.  The
450da6c28aaSamw 		 * other lock request threads waiting on this lock will go
451da6c28aaSamw 		 * back to sleep when they discover they are still blocked.
452da6c28aaSamw 		 */
453da6c28aaSamw 		sr->sr_state = SMB_REQ_STATE_CANCELED;
454da6c28aaSamw 
455da6c28aaSamw 		ASSERT(sr->sr_awaiting != NULL);
456da6c28aaSamw 		mutex_enter(&sr->sr_awaiting->l_mutex);
457da6c28aaSamw 		cv_broadcast(&sr->sr_awaiting->l_cv);
458da6c28aaSamw 		mutex_exit(&sr->sr_awaiting->l_mutex);
459da6c28aaSamw 
460da6c28aaSamw 		break;
461da6c28aaSamw 
462da6c28aaSamw 	case SMB_REQ_STATE_WAITING_EVENT:
463da6c28aaSamw 	case SMB_REQ_STATE_EVENT_OCCURRED:
464da6c28aaSamw 		/*
465da6c28aaSamw 		 * Cancellations for these states are handled by the
466da6c28aaSamw 		 * notify-change code
467da6c28aaSamw 		 */
468da6c28aaSamw 		break;
469da6c28aaSamw 
470da6c28aaSamw 	case SMB_REQ_STATE_COMPLETED:
471da6c28aaSamw 	case SMB_REQ_STATE_CANCELED:
472da6c28aaSamw 		/*
473da6c28aaSamw 		 * No action required for these states since the request
474da6c28aaSamw 		 * is completing.
475da6c28aaSamw 		 */
476da6c28aaSamw 		break;
477da6c28aaSamw 	/*
478da6c28aaSamw 	 * Cases included:
479da6c28aaSamw 	 *	SMB_REQ_STATE_FREE:
480da6c28aaSamw 	 *	SMB_REQ_STATE_INITIALIZING:
481da6c28aaSamw 	 */
482da6c28aaSamw 	default:
483da6c28aaSamw 		ASSERT(0);
484da6c28aaSamw 		break;
485da6c28aaSamw 	}
486da6c28aaSamw 	mutex_exit(&sr->sr_mutex);
487da6c28aaSamw }
488da6c28aaSamw 
489da6c28aaSamw /*
490da6c28aaSamw  * This is the entry point for processing SMB messages over NetBIOS or
491da6c28aaSamw  * SMB-over-TCP.
492da6c28aaSamw  *
493da6c28aaSamw  * NetBIOS connections require a session request to establish a session
494da6c28aaSamw  * on which to send session messages.
495da6c28aaSamw  *
496da6c28aaSamw  * Session requests are not valid on SMB-over-TCP.  We don't need to do
497da6c28aaSamw  * anything here as session requests will be treated as an error when
498da6c28aaSamw  * handling session messages.
499da6c28aaSamw  */
500*faa1795aSjb int
501*faa1795aSjb smb_session_daemon(smb_session_list_t *se)
502da6c28aaSamw {
503*faa1795aSjb 	int		rc = 0;
504*faa1795aSjb 	smb_session_t	*session;
505da6c28aaSamw 
506*faa1795aSjb 	session = smb_session_list_activate_head(se);
507*faa1795aSjb 	if (session == NULL)
508*faa1795aSjb 		return (EINVAL);
509da6c28aaSamw 
510*faa1795aSjb 	if (session->s_local_port == SSN_SRVC_TCP_PORT) {
511da6c28aaSamw 		rc = smb_session_request(session);
512*faa1795aSjb 		if (rc) {
513*faa1795aSjb 			smb_rwx_rwenter(&session->s_lock, RW_WRITER);
514*faa1795aSjb 			session->s_state = SMB_SESSION_STATE_DISCONNECTED;
515*faa1795aSjb 			smb_rwx_rwexit(&session->s_lock);
516*faa1795aSjb 			smb_session_list_terminate(se, session);
517*faa1795aSjb 			return (rc);
518*faa1795aSjb 		}
519*faa1795aSjb 	}
520da6c28aaSamw 
521da6c28aaSamw 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
522*faa1795aSjb 	session->s_state = SMB_SESSION_STATE_ESTABLISHED;
523*faa1795aSjb 	smb_rwx_rwexit(&session->s_lock);
524da6c28aaSamw 
525*faa1795aSjb 	rc = smb_session_message(session);
526da6c28aaSamw 
527*faa1795aSjb 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
528da6c28aaSamw 	session->s_state = SMB_SESSION_STATE_DISCONNECTED;
529da6c28aaSamw 	smb_rwx_rwexit(&session->s_lock);
530da6c28aaSamw 
531*faa1795aSjb 	smb_soshutdown(session->sock);
532*faa1795aSjb 
533da6c28aaSamw 	DTRACE_PROBE2(session__drop, struct session *, session, int, rc);
534da6c28aaSamw 
535da6c28aaSamw 	smb_session_cancel(session);
536da6c28aaSamw 
537da6c28aaSamw 	/*
538da6c28aaSamw 	 * At this point everything related to the session should have been
539da6c28aaSamw 	 * cleaned up and we expect that nothing will attempt to use the
540da6c28aaSamw 	 * socket.
541da6c28aaSamw 	 */
542*faa1795aSjb 	smb_session_list_terminate(se, session);
543da6c28aaSamw 
544*faa1795aSjb 	return (rc);
545da6c28aaSamw }
546da6c28aaSamw 
547da6c28aaSamw /*
548da6c28aaSamw  * Read and process SMB requests.
549da6c28aaSamw  *
550da6c28aaSamw  * Returns:
551da6c28aaSamw  *	0	Success
552da6c28aaSamw  *	1	Unable to read transport header
553da6c28aaSamw  *	2	Invalid transport header type
554da6c28aaSamw  *	3	Invalid SMB length (too small)
555da6c28aaSamw  *	4	Unable to read SMB header
556da6c28aaSamw  *	5	Invalid SMB header (bad magic number)
557da6c28aaSamw  *	6	Unable to read SMB data
558da6c28aaSamw  *	2x	Write raw failed
559da6c28aaSamw  */
560da6c28aaSamw static int
561da6c28aaSamw smb_session_message(smb_session_t *session)
562da6c28aaSamw {
563*faa1795aSjb 	smb_request_t	*sr = NULL;
564*faa1795aSjb 	smb_xprt_t	hdr;
565*faa1795aSjb 	uint8_t		*req_buf;
566*faa1795aSjb 	uint32_t	resid;
567*faa1795aSjb 	int		rc;
568da6c28aaSamw 
569*faa1795aSjb 	for (;;) {
570*faa1795aSjb 
571*faa1795aSjb 		rc = smb_session_xprt_gethdr(session, &hdr);
572*faa1795aSjb 		if (rc)
573*faa1795aSjb 			return (rc);
574*faa1795aSjb 
575*faa1795aSjb 		DTRACE_PROBE2(session__receive__xprthdr, session_t *, session,
576*faa1795aSjb 		    smb_xprt_t *, &hdr);
577*faa1795aSjb 
578*faa1795aSjb 		if (hdr.xh_type != SESSION_MESSAGE) {
579*faa1795aSjb 			/*
580*faa1795aSjb 			 * Anything other than SESSION_MESSAGE or
581*faa1795aSjb 			 * SESSION_KEEP_ALIVE is an error.  A SESSION_REQUEST
582*faa1795aSjb 			 * may indicate a new session request but we need to
583*faa1795aSjb 			 * close this session and we can treat it as an error
584*faa1795aSjb 			 * here.
585*faa1795aSjb 			 */
586*faa1795aSjb 			if (hdr.xh_type == SESSION_KEEP_ALIVE) {
587*faa1795aSjb 				session->keep_alive = smb_keep_alive;
588da6c28aaSamw 				continue;
589da6c28aaSamw 			}
590*faa1795aSjb 			return (EPROTO);
591da6c28aaSamw 		}
592da6c28aaSamw 
593*faa1795aSjb 		if (hdr.xh_length < SMB_HEADER_LEN)
594*faa1795aSjb 			return (EPROTO);
595da6c28aaSamw 
596*faa1795aSjb 		session->keep_alive = smb_keep_alive;
597da6c28aaSamw 
598da6c28aaSamw 		/*
599*faa1795aSjb 		 * Allocate a request context, read the SMB header and validate
600*faa1795aSjb 		 * it. The sr includes a buffer large enough to hold the SMB
601*faa1795aSjb 		 * request payload.  If the header looks valid, read any
602*faa1795aSjb 		 * remaining data.
603da6c28aaSamw 		 */
604*faa1795aSjb 		sr = smb_request_alloc(session, hdr.xh_length);
605da6c28aaSamw 
606*faa1795aSjb 		req_buf = (uint8_t *)sr->sr_request_buf;
607*faa1795aSjb 		resid = hdr.xh_length;
608da6c28aaSamw 
609*faa1795aSjb 		rc = smb_sorecv(session->sock, req_buf, SMB_HEADER_LEN);
610*faa1795aSjb 		if (rc) {
611*faa1795aSjb 			smb_request_free(sr);
612*faa1795aSjb 			return (rc);
613*faa1795aSjb 		}
614da6c28aaSamw 
615*faa1795aSjb 		if (SMB_PROTOCOL_MAGIC_INVALID(sr)) {
616*faa1795aSjb 			smb_request_free(sr);
617*faa1795aSjb 			return (EPROTO);
618*faa1795aSjb 		}
619da6c28aaSamw 
620*faa1795aSjb 		if (resid > SMB_HEADER_LEN) {
621*faa1795aSjb 			req_buf += SMB_HEADER_LEN;
622*faa1795aSjb 			resid -= SMB_HEADER_LEN;
623da6c28aaSamw 
624*faa1795aSjb 			rc = smb_sorecv(session->sock, req_buf, resid);
625*faa1795aSjb 			if (rc) {
626*faa1795aSjb 				smb_request_free(sr);
627*faa1795aSjb 				return (rc);
628da6c28aaSamw 			}
629da6c28aaSamw 		}
630da6c28aaSamw 
631*faa1795aSjb 		/*
632*faa1795aSjb 		 * Initialize command MBC to represent the received data.
633*faa1795aSjb 		 */
634*faa1795aSjb 		smb_request_init_command_mbuf(sr);
635da6c28aaSamw 
636*faa1795aSjb 		DTRACE_PROBE1(session__receive__smb, smb_request_t *, sr);
637da6c28aaSamw 
638da6c28aaSamw 		/*
639*faa1795aSjb 		 * If this is a raw write, hand off the request.  The handler
640*faa1795aSjb 		 * will retrieve the remaining raw data and process the request.
641da6c28aaSamw 		 */
642*faa1795aSjb 		if (SMB_IS_WRITERAW(sr)) {
643*faa1795aSjb 			rc = smb_handle_write_raw(session, sr);
644*faa1795aSjb 			/* XXX smb_request_free(sr); ??? */
645*faa1795aSjb 			return (rc);
646*faa1795aSjb 		}
647da6c28aaSamw 
648*faa1795aSjb 		sr->sr_state = SMB_REQ_STATE_SUBMITTED;
649*faa1795aSjb 		(void) taskq_dispatch(session->s_server->sv_thread_pool,
650*faa1795aSjb 		    smb_session_worker, sr, TQ_SLEEP);
651da6c28aaSamw 	}
652da6c28aaSamw }
653da6c28aaSamw 
654da6c28aaSamw /*
655da6c28aaSamw  * smb_session_reject
656da6c28aaSamw  *
657da6c28aaSamw  * Build and send a NEGATIVE_SESSION_RESPONSE on the specified socket.
658da6c28aaSamw  * The reason is written to the log.
659da6c28aaSamw  */
660da6c28aaSamw /*ARGSUSED*/
661da6c28aaSamw void
662da6c28aaSamw smb_session_reject(smb_session_t *session, char *reason)
663da6c28aaSamw {
6645cdbe942Sjb 	smb_txbuf_t	*txb;
665da6c28aaSamw 
666da6c28aaSamw 	smb_rwx_rwenter(&session->s_lock, RW_READER);
667da6c28aaSamw 	if (session->sock != NULL) {
6685cdbe942Sjb 		txb = smb_net_txb_alloc();
6695cdbe942Sjb 		txb->tb_data[0] = NEGATIVE_SESSION_RESPONSE;
6705cdbe942Sjb 		txb->tb_data[1] = 0;
6715cdbe942Sjb 		txb->tb_data[2] = 0;
6725cdbe942Sjb 		txb->tb_data[3] = 1;
6735cdbe942Sjb 		txb->tb_data[4] = SESSION_INSUFFICIENT_RESOURCES;
6745cdbe942Sjb 		txb->tb_len = 5;
6755cdbe942Sjb 		(void) smb_net_txb_send(session->sock, &session->s_txlst, txb);
676da6c28aaSamw 	}
677da6c28aaSamw 	smb_rwx_rwexit(&session->s_lock);
678da6c28aaSamw }
679da6c28aaSamw 
680da6c28aaSamw /*
681da6c28aaSamw  * Port will be SSN_SRVC_TCP_PORT or SMB_SRVC_TCP_PORT.
682da6c28aaSamw  */
683da6c28aaSamw smb_session_t *
684*faa1795aSjb smb_session_create(struct sonode *new_so, uint16_t port, smb_server_t *sv)
685da6c28aaSamw {
686da6c28aaSamw 	uint32_t		ipaddr;
687da6c28aaSamw 	uint32_t		local_ipaddr;
688da6c28aaSamw 	struct sockaddr_in	sin;
689da6c28aaSamw 	smb_session_t		*session;
690da6c28aaSamw 
691*faa1795aSjb 	session = kmem_cache_alloc(sv->si_cache_session, KM_SLEEP);
692da6c28aaSamw 	bzero(session, sizeof (smb_session_t));
693da6c28aaSamw 
694da6c28aaSamw 	if (smb_idpool_constructor(&session->s_uid_pool)) {
695*faa1795aSjb 		kmem_cache_free(sv->si_cache_session, session);
696da6c28aaSamw 		return (NULL);
697da6c28aaSamw 	}
698da6c28aaSamw 
699da6c28aaSamw 	session->s_kid = SMB_NEW_KID();
700*faa1795aSjb 	session->s_state = SMB_SESSION_STATE_INITIALIZED;
701da6c28aaSamw 	session->native_os = NATIVE_OS_UNKNOWN;
702da6c28aaSamw 	session->opentime = lbolt64;
703da6c28aaSamw 	session->keep_alive = smb_keep_alive;
704da6c28aaSamw 	session->activity_timestamp = lbolt64;
705da6c28aaSamw 
706da6c28aaSamw 	smb_slist_constructor(&session->s_req_list, sizeof (smb_request_t),
707da6c28aaSamw 	    offsetof(smb_request_t, sr_session_lnd));
708da6c28aaSamw 
709da6c28aaSamw 	smb_llist_constructor(&session->s_user_list, sizeof (smb_user_t),
710da6c28aaSamw 	    offsetof(smb_user_t, u_lnd));
711da6c28aaSamw 
712da6c28aaSamw 	smb_llist_constructor(&session->s_xa_list, sizeof (smb_xa_t),
713da6c28aaSamw 	    offsetof(smb_xa_t, xa_lnd));
714da6c28aaSamw 
7155cdbe942Sjb 	smb_net_txl_constructor(&session->s_txlst);
7165cdbe942Sjb 
717da6c28aaSamw 	smb_rwx_init(&session->s_lock);
718da6c28aaSamw 
719*faa1795aSjb 	if (new_so) {
720*faa1795aSjb 		bcopy(new_so->so_faddr_sa, &sin, new_so->so_faddr_len);
721*faa1795aSjb 		ipaddr = sin.sin_addr.s_addr;
722*faa1795aSjb 		bcopy(new_so->so_laddr_sa, &sin, new_so->so_faddr_len);
723*faa1795aSjb 		local_ipaddr = sin.sin_addr.s_addr;
724*faa1795aSjb 		session->s_local_port = port;
725*faa1795aSjb 		session->ipaddr = ipaddr;
726*faa1795aSjb 		session->local_ipaddr = local_ipaddr;
727*faa1795aSjb 		session->sock = new_so;
728*faa1795aSjb 	}
729da6c28aaSamw 
730*faa1795aSjb 	session->s_server = sv;
731*faa1795aSjb 	smb_server_get_cfg(sv, &session->s_cfg);
732*faa1795aSjb 	session->s_cache_request = sv->si_cache_request;
733*faa1795aSjb 	session->s_cache = sv->si_cache_session;
734da6c28aaSamw 	session->s_magic = SMB_SESSION_MAGIC;
735da6c28aaSamw 	return (session);
736da6c28aaSamw }
737da6c28aaSamw 
738da6c28aaSamw void
739da6c28aaSamw smb_session_delete(smb_session_t *session)
740da6c28aaSamw {
741da6c28aaSamw 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
742da6c28aaSamw 
743da6c28aaSamw 	session->s_magic = (uint32_t)~SMB_SESSION_MAGIC;
744da6c28aaSamw 
745da6c28aaSamw 	smb_rwx_destroy(&session->s_lock);
7465cdbe942Sjb 	smb_net_txl_destructor(&session->s_txlst);
747da6c28aaSamw 	smb_slist_destructor(&session->s_req_list);
748da6c28aaSamw 	smb_llist_destructor(&session->s_user_list);
749da6c28aaSamw 	smb_llist_destructor(&session->s_xa_list);
750da6c28aaSamw 
751da6c28aaSamw 	ASSERT(session->s_tree_cnt == 0);
752da6c28aaSamw 	ASSERT(session->s_file_cnt == 0);
753da6c28aaSamw 	ASSERT(session->s_dir_cnt == 0);
754da6c28aaSamw 
755da6c28aaSamw 	smb_idpool_destructor(&session->s_uid_pool);
756*faa1795aSjb 	kmem_cache_free(session->s_cache, session);
757da6c28aaSamw }
758da6c28aaSamw 
759da6c28aaSamw void
760da6c28aaSamw smb_session_cancel(smb_session_t *session)
761da6c28aaSamw {
762da6c28aaSamw 	smb_xa_t	*xa, *nextxa;
763da6c28aaSamw 
764da6c28aaSamw 	/* All the request currently being treated must be canceled. */
765da6c28aaSamw 	smb_session_cancel_requests(session);
766da6c28aaSamw 
767da6c28aaSamw 	/*
768da6c28aaSamw 	 * We wait for the completion of all the requests associated with
769da6c28aaSamw 	 * this session.
770da6c28aaSamw 	 */
771da6c28aaSamw 	smb_slist_wait_for_empty(&session->s_req_list);
772da6c28aaSamw 
773da6c28aaSamw 	/*
774da6c28aaSamw 	 * At this point the reference count of the users, trees, files,
775da6c28aaSamw 	 * directories should be zero. It should be possible to destroy them
776da6c28aaSamw 	 * without any problem.
777da6c28aaSamw 	 */
778da6c28aaSamw 	xa = smb_llist_head(&session->s_xa_list);
779da6c28aaSamw 	while (xa) {
780da6c28aaSamw 		nextxa = smb_llist_next(&session->s_xa_list, xa);
781da6c28aaSamw 		smb_xa_close(xa);
782da6c28aaSamw 		xa = nextxa;
783da6c28aaSamw 	}
784da6c28aaSamw 	smb_user_logoff_all(session);
785da6c28aaSamw }
786da6c28aaSamw 
787da6c28aaSamw void
788da6c28aaSamw smb_session_cancel_requests(
789da6c28aaSamw     smb_session_t	*session)
790da6c28aaSamw {
791da6c28aaSamw 	smb_request_t	*sr;
792da6c28aaSamw 	smb_request_t	*tmp;
793da6c28aaSamw 
794da6c28aaSamw 	/* All the SMB requests on the notification queue are canceled. */
795da6c28aaSamw 	smb_process_session_notify_change_queue(session);
796da6c28aaSamw 
797da6c28aaSamw 	smb_slist_enter(&session->s_req_list);
798da6c28aaSamw 	sr = smb_slist_head(&session->s_req_list);
799da6c28aaSamw 	while (sr) {
800da6c28aaSamw 		ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
801da6c28aaSamw 		tmp = smb_slist_next(&session->s_req_list, sr);
802da6c28aaSamw 
803da6c28aaSamw 		smb_request_cancel(sr);
804da6c28aaSamw 
805da6c28aaSamw 		sr = tmp;
806da6c28aaSamw 	}
807da6c28aaSamw 	smb_slist_exit(&session->s_req_list);
808da6c28aaSamw }
809da6c28aaSamw 
810da6c28aaSamw void
811da6c28aaSamw smb_session_worker(
812da6c28aaSamw     void	*arg)
813da6c28aaSamw {
814da6c28aaSamw 	smb_request_t	*sr;
815da6c28aaSamw 
816da6c28aaSamw 	sr = (smb_request_t *)arg;
817da6c28aaSamw 
818da6c28aaSamw 	ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
819da6c28aaSamw 
820*faa1795aSjb 
821da6c28aaSamw 	mutex_enter(&sr->sr_mutex);
822da6c28aaSamw 	switch (sr->sr_state) {
823da6c28aaSamw 	case SMB_REQ_STATE_SUBMITTED:
824da6c28aaSamw 		mutex_exit(&sr->sr_mutex);
8257b59d02dSjb 		smb_dispatch_request(sr);
826da6c28aaSamw 		mutex_enter(&sr->sr_mutex);
827da6c28aaSamw 		if (!sr->sr_keep) {
828da6c28aaSamw 			sr->sr_state = SMB_REQ_STATE_COMPLETED;
829da6c28aaSamw 			mutex_exit(&sr->sr_mutex);
830da6c28aaSamw 			smb_request_free(sr);
831da6c28aaSamw 			break;
832da6c28aaSamw 		}
833da6c28aaSamw 		mutex_exit(&sr->sr_mutex);
834da6c28aaSamw 		break;
835da6c28aaSamw 
836da6c28aaSamw 	default:
837da6c28aaSamw 		ASSERT(sr->sr_state == SMB_REQ_STATE_CANCELED);
838da6c28aaSamw 		sr->sr_state = SMB_REQ_STATE_COMPLETED;
839da6c28aaSamw 		mutex_exit(&sr->sr_mutex);
840da6c28aaSamw 		smb_request_free(sr);
841da6c28aaSamw 		break;
842da6c28aaSamw 	}
843da6c28aaSamw }
844da6c28aaSamw 
845da6c28aaSamw /*
846da6c28aaSamw  * smb_session_disconnect_share
847da6c28aaSamw  *
848da6c28aaSamw  * Disconnects the specified share. This function should be called after the
849da6c28aaSamw  * share passed in has been made unavailable by the "share manager".
850da6c28aaSamw  */
851da6c28aaSamw void
852*faa1795aSjb smb_session_disconnect_share(smb_session_list_t *se, char *sharename)
853da6c28aaSamw {
854da6c28aaSamw 	smb_session_t	*session;
855da6c28aaSamw 
856*faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
857*faa1795aSjb 	session = list_head(&se->se_act.lst);
858*faa1795aSjb 	while (session) {
859da6c28aaSamw 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
860da6c28aaSamw 		smb_rwx_rwenter(&session->s_lock, RW_READER);
861da6c28aaSamw 		switch (session->s_state) {
862da6c28aaSamw 		case SMB_SESSION_STATE_NEGOTIATED:
863da6c28aaSamw 		case SMB_SESSION_STATE_OPLOCK_BREAKING:
864da6c28aaSamw 		case SMB_SESSION_STATE_WRITE_RAW_ACTIVE: {
865da6c28aaSamw 			smb_user_t	*user;
866da6c28aaSamw 			smb_user_t	*next;
867da6c28aaSamw 
868da6c28aaSamw 			user = smb_user_lookup_by_state(session, NULL);
869da6c28aaSamw 			while (user) {
870da6c28aaSamw 				smb_user_disconnect_share(user, sharename);
871da6c28aaSamw 				next = smb_user_lookup_by_state(session, user);
872da6c28aaSamw 				smb_user_release(user);
873da6c28aaSamw 				user = next;
874da6c28aaSamw 			}
875da6c28aaSamw 			break;
876da6c28aaSamw 
877da6c28aaSamw 		}
878da6c28aaSamw 		default:
879da6c28aaSamw 			break;
880da6c28aaSamw 		}
881da6c28aaSamw 		smb_rwx_rwexit(&session->s_lock);
882*faa1795aSjb 		session = list_next(&se->se_act.lst, session);
883da6c28aaSamw 	}
884*faa1795aSjb 	rw_exit(&se->se_lock);
885da6c28aaSamw }
886da6c28aaSamw 
887da6c28aaSamw /*
888da6c28aaSamw  * smb_session_disconnect_volume
889da6c28aaSamw  *
890da6c28aaSamw  * This function is called when a volume is deleted. We need to ensure
891da6c28aaSamw  * all trees with a reference to the volume are destroyed before we
892da6c28aaSamw  * discard the fs_online. Before destroying each tree, we notify any
893da6c28aaSamw  * in-progress requests and give them a chance to complete.
894da6c28aaSamw  *
895da6c28aaSamw  * NOTE:
896da6c28aaSamw  * We shouldn't be accepting any new connection on this volume while
897da6c28aaSamw  * we are in this function.
898da6c28aaSamw  */
899da6c28aaSamw void
900*faa1795aSjb smb_session_disconnect_volume(smb_session_list_t *se, fs_desc_t *fsd)
901da6c28aaSamw {
902da6c28aaSamw 	smb_session_t	*session;
903da6c28aaSamw 
904*faa1795aSjb 	rw_enter(&se->se_lock, RW_READER);
905*faa1795aSjb 	session = list_head(&se->se_act.lst);
906*faa1795aSjb 	while (session) {
907da6c28aaSamw 
908da6c28aaSamw 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
909da6c28aaSamw 		smb_rwx_rwenter(&session->s_lock, RW_READER);
910da6c28aaSamw 		switch (session->s_state) {
911da6c28aaSamw 		case SMB_SESSION_STATE_NEGOTIATED:
912da6c28aaSamw 		case SMB_SESSION_STATE_OPLOCK_BREAKING:
913da6c28aaSamw 		case SMB_SESSION_STATE_WRITE_RAW_ACTIVE: {
914da6c28aaSamw 			smb_user_t	*user;
915da6c28aaSamw 			smb_user_t	*next;
916da6c28aaSamw 
917da6c28aaSamw 			user = smb_user_lookup_by_state(session, NULL);
918da6c28aaSamw 			while (user) {
919da6c28aaSamw 				smb_user_disconnect_volume(user, fsd);
920da6c28aaSamw 				next = smb_user_lookup_by_state(session, user);
921da6c28aaSamw 				smb_user_release(user);
922da6c28aaSamw 				user = next;
923da6c28aaSamw 			}
924da6c28aaSamw 			break;
925da6c28aaSamw 
926da6c28aaSamw 		}
927da6c28aaSamw 		default:
928da6c28aaSamw 			break;
929da6c28aaSamw 		}
930da6c28aaSamw 		smb_rwx_rwexit(&session->s_lock);
931*faa1795aSjb 		session = list_next(&se->se_act.lst, session);
932*faa1795aSjb 	}
933*faa1795aSjb 	rw_exit(&se->se_lock);
934*faa1795aSjb }
935*faa1795aSjb 
936*faa1795aSjb void
937*faa1795aSjb smb_session_list_constructor(smb_session_list_t *se)
938*faa1795aSjb {
939*faa1795aSjb 	bzero(se, sizeof (*se));
940*faa1795aSjb 	rw_init(&se->se_lock, NULL, RW_DEFAULT, NULL);
941*faa1795aSjb 	list_create(&se->se_rdy.lst, sizeof (smb_session_t),
942*faa1795aSjb 	    offsetof(smb_session_t, s_lnd));
943*faa1795aSjb 	list_create(&se->se_act.lst, sizeof (smb_session_t),
944*faa1795aSjb 	    offsetof(smb_session_t, s_lnd));
945*faa1795aSjb }
946*faa1795aSjb 
947*faa1795aSjb void
948*faa1795aSjb smb_session_list_destructor(smb_session_list_t *se)
949*faa1795aSjb {
950*faa1795aSjb 	list_destroy(&se->se_rdy.lst);
951*faa1795aSjb 	list_destroy(&se->se_act.lst);
952*faa1795aSjb 	rw_destroy(&se->se_lock);
953*faa1795aSjb }
954*faa1795aSjb 
955*faa1795aSjb void
956*faa1795aSjb smb_session_list_append(smb_session_list_t *se, smb_session_t *session)
957*faa1795aSjb {
958*faa1795aSjb 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
959*faa1795aSjb 	ASSERT(session->s_state == SMB_SESSION_STATE_INITIALIZED);
960*faa1795aSjb 
961*faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
962*faa1795aSjb 	list_insert_tail(&se->se_rdy.lst, session);
963*faa1795aSjb 	se->se_rdy.count++;
964*faa1795aSjb 	se->se_wrop++;
965*faa1795aSjb 	rw_exit(&se->se_lock);
966*faa1795aSjb }
967*faa1795aSjb 
968*faa1795aSjb void
969*faa1795aSjb smb_session_list_delete_tail(smb_session_list_t *se)
970*faa1795aSjb {
971*faa1795aSjb 	smb_session_t	*session;
972*faa1795aSjb 
973*faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
974*faa1795aSjb 	session = list_tail(&se->se_rdy.lst);
975*faa1795aSjb 	if (session) {
976*faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
977*faa1795aSjb 		ASSERT(session->s_state == SMB_SESSION_STATE_INITIALIZED);
978*faa1795aSjb 		list_remove(&se->se_rdy.lst, session);
979*faa1795aSjb 		ASSERT(se->se_rdy.count);
980*faa1795aSjb 		se->se_rdy.count--;
981*faa1795aSjb 		rw_exit(&se->se_lock);
982*faa1795aSjb 		smb_session_delete(session);
983*faa1795aSjb 		return;
984*faa1795aSjb 	}
985*faa1795aSjb 	rw_exit(&se->se_lock);
986*faa1795aSjb }
987*faa1795aSjb 
988*faa1795aSjb smb_session_t *
989*faa1795aSjb smb_session_list_activate_head(smb_session_list_t *se)
990*faa1795aSjb {
991*faa1795aSjb 	smb_session_t	*session;
992*faa1795aSjb 
993*faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
994*faa1795aSjb 	session = list_head(&se->se_rdy.lst);
995*faa1795aSjb 	if (session) {
996*faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
997*faa1795aSjb 		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
998*faa1795aSjb 		ASSERT(session->s_state == SMB_SESSION_STATE_INITIALIZED);
999*faa1795aSjb 		session->s_thread = curthread;
1000*faa1795aSjb 		session->s_ktdid = session->s_thread->t_did;
1001*faa1795aSjb 		smb_rwx_rwexit(&session->s_lock);
1002*faa1795aSjb 		list_remove(&se->se_rdy.lst, session);
1003*faa1795aSjb 		se->se_rdy.count--;
1004*faa1795aSjb 		list_insert_tail(&se->se_act.lst, session);
1005*faa1795aSjb 		se->se_act.count++;
1006*faa1795aSjb 		se->se_wrop++;
1007*faa1795aSjb 	}
1008*faa1795aSjb 	rw_exit(&se->se_lock);
1009*faa1795aSjb 	return (session);
1010*faa1795aSjb }
1011*faa1795aSjb 
1012*faa1795aSjb void
1013*faa1795aSjb smb_session_list_terminate(smb_session_list_t *se, smb_session_t *session)
1014*faa1795aSjb {
1015*faa1795aSjb 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
1016*faa1795aSjb 
1017*faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
1018*faa1795aSjb 
1019*faa1795aSjb 	smb_rwx_rwenter(&session->s_lock, RW_WRITER);
1020*faa1795aSjb 	ASSERT(session->s_state == SMB_SESSION_STATE_DISCONNECTED);
1021*faa1795aSjb 	session->s_state = SMB_SESSION_STATE_TERMINATED;
1022*faa1795aSjb 	smb_sodestroy(session->sock);
1023*faa1795aSjb 	session->sock = NULL;
1024*faa1795aSjb 	smb_rwx_rwexit(&session->s_lock);
1025*faa1795aSjb 
1026*faa1795aSjb 	list_remove(&se->se_act.lst, session);
1027*faa1795aSjb 	se->se_act.count--;
1028*faa1795aSjb 	se->se_wrop++;
1029*faa1795aSjb 
1030*faa1795aSjb 	ASSERT(session->s_thread == curthread);
1031*faa1795aSjb 
1032*faa1795aSjb 	rw_exit(&se->se_lock);
1033*faa1795aSjb 
1034*faa1795aSjb 	smb_session_delete(session);
1035*faa1795aSjb }
1036*faa1795aSjb 
1037*faa1795aSjb /*
1038*faa1795aSjb  * smb_session_list_signal
1039*faa1795aSjb  *
1040*faa1795aSjb  * This function signals all the session threads. The intent is to terminate
1041*faa1795aSjb  * them. The sessions still in the SMB_SESSION_STATE_INITIALIZED are delete
1042*faa1795aSjb  * immediately.
1043*faa1795aSjb  *
1044*faa1795aSjb  * This function must only be called by the threads listening and accepting
1045*faa1795aSjb  * connections. They must pass in their respective session list.
1046*faa1795aSjb  */
1047*faa1795aSjb void
1048*faa1795aSjb smb_session_list_signal(smb_session_list_t *se)
1049*faa1795aSjb {
1050*faa1795aSjb 	smb_session_t	*session;
1051*faa1795aSjb 
1052*faa1795aSjb 	rw_enter(&se->se_lock, RW_WRITER);
1053*faa1795aSjb 	while (session = list_head(&se->se_rdy.lst)) {
1054*faa1795aSjb 
1055*faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
1056*faa1795aSjb 
1057*faa1795aSjb 		smb_rwx_rwenter(&session->s_lock, RW_WRITER);
1058*faa1795aSjb 		ASSERT(session->s_state == SMB_SESSION_STATE_INITIALIZED);
1059*faa1795aSjb 		session->s_state = SMB_SESSION_STATE_TERMINATED;
1060*faa1795aSjb 		smb_sodestroy(session->sock);
1061*faa1795aSjb 		session->sock = NULL;
1062*faa1795aSjb 		smb_rwx_rwexit(&session->s_lock);
1063*faa1795aSjb 
1064*faa1795aSjb 		list_remove(&se->se_rdy.lst, session);
1065*faa1795aSjb 		se->se_rdy.count--;
1066*faa1795aSjb 		se->se_wrop++;
1067*faa1795aSjb 
1068*faa1795aSjb 		rw_exit(&se->se_lock);
1069*faa1795aSjb 		smb_session_delete(session);
1070*faa1795aSjb 		rw_enter(&se->se_lock, RW_WRITER);
1071da6c28aaSamw 	}
1072*faa1795aSjb 	rw_downgrade(&se->se_lock);
1073*faa1795aSjb 
1074*faa1795aSjb 	session = list_head(&se->se_act.lst);
1075*faa1795aSjb 	while (session) {
1076*faa1795aSjb 
1077*faa1795aSjb 		ASSERT(session->s_magic == SMB_SESSION_MAGIC);
1078*faa1795aSjb 		tsignal(session->s_thread, SIGINT);
1079*faa1795aSjb 		session = list_next(&se->se_act.lst, session);
1080*faa1795aSjb 	}
1081*faa1795aSjb 	rw_exit(&se->se_lock);
1082*faa1795aSjb }
1083*faa1795aSjb 
1084*faa1795aSjb /*
1085*faa1795aSjb  * smb_request_alloc
1086*faa1795aSjb  *
1087*faa1795aSjb  * Allocate an smb_request_t structure from the kmem_cache.  Partially
1088*faa1795aSjb  * initialize the found/new request.
1089*faa1795aSjb  *
1090*faa1795aSjb  * Returns pointer to a request
1091*faa1795aSjb  */
1092*faa1795aSjb smb_request_t *
1093*faa1795aSjb smb_request_alloc(smb_session_t *session, int req_length)
1094*faa1795aSjb {
1095*faa1795aSjb 	smb_request_t	*sr;
1096*faa1795aSjb 
1097*faa1795aSjb 	ASSERT(session->s_magic == SMB_SESSION_MAGIC);
1098*faa1795aSjb 
1099*faa1795aSjb 	sr = kmem_cache_alloc(session->s_cache_request, KM_SLEEP);
1100*faa1795aSjb 
1101*faa1795aSjb 	/*
1102*faa1795aSjb 	 * Future:  Use constructor to pre-initialize some fields.  For now
1103*faa1795aSjb 	 * there are so many fields that it is easiest just to zero the
1104*faa1795aSjb 	 * whole thing and start over.
1105*faa1795aSjb 	 */
1106*faa1795aSjb 	bzero(sr, sizeof (smb_request_t));
1107*faa1795aSjb 
1108*faa1795aSjb 	mutex_init(&sr->sr_mutex, NULL, MUTEX_DEFAULT, NULL);
1109*faa1795aSjb 	sr->session = session;
1110*faa1795aSjb 	sr->sr_server = session->s_server;
1111*faa1795aSjb 	sr->sr_gmtoff = session->s_server->si_gmtoff;
1112*faa1795aSjb 	sr->sr_cache = session->s_server->si_cache_request;
1113*faa1795aSjb 	sr->sr_cfg = &session->s_cfg;
1114*faa1795aSjb 	sr->request_storage.forw = &sr->request_storage;
1115*faa1795aSjb 	sr->request_storage.back = &sr->request_storage;
1116*faa1795aSjb 	sr->command.max_bytes = req_length;
1117*faa1795aSjb 	sr->reply.max_bytes = smb_maxbufsize;
1118*faa1795aSjb 	sr->sr_req_length = req_length;
1119*faa1795aSjb 	if (req_length)
1120*faa1795aSjb 		sr->sr_request_buf = kmem_alloc(req_length, KM_SLEEP);
1121*faa1795aSjb 	sr->sr_magic = SMB_REQ_MAGIC;
1122*faa1795aSjb 	sr->sr_state = SMB_REQ_STATE_INITIALIZING;
1123*faa1795aSjb 	smb_slist_insert_tail(&session->s_req_list, sr);
1124*faa1795aSjb 	return (sr);
1125*faa1795aSjb }
1126*faa1795aSjb 
1127*faa1795aSjb /*
1128*faa1795aSjb  * smb_request_free
1129*faa1795aSjb  *
1130*faa1795aSjb  * release the memories which have been allocated for a smb request.
1131*faa1795aSjb  */
1132*faa1795aSjb void
1133*faa1795aSjb smb_request_free(smb_request_t *sr)
1134*faa1795aSjb {
1135*faa1795aSjb 	ASSERT(sr->sr_magic == SMB_REQ_MAGIC);
1136*faa1795aSjb 	ASSERT(sr->session);
1137*faa1795aSjb 	ASSERT(sr->fid_ofile == NULL);
1138*faa1795aSjb 	ASSERT(sr->sid_odir == NULL);
1139*faa1795aSjb 	ASSERT(sr->r_xa == NULL);
1140*faa1795aSjb 
1141*faa1795aSjb 	if (sr->tid_tree)
1142*faa1795aSjb 		smb_tree_release(sr->tid_tree);
1143*faa1795aSjb 
1144*faa1795aSjb 	if (sr->uid_user)
1145*faa1795aSjb 		smb_user_release(sr->uid_user);
1146*faa1795aSjb 
1147*faa1795aSjb 	smb_slist_remove(&sr->session->s_req_list, sr);
1148*faa1795aSjb 
1149*faa1795aSjb 	sr->session = NULL;
1150*faa1795aSjb 
1151*faa1795aSjb 	/* Release any temp storage */
1152*faa1795aSjb 	smbsr_free_malloc_list(&sr->request_storage);
1153*faa1795aSjb 
1154*faa1795aSjb 	if (sr->sr_request_buf)
1155*faa1795aSjb 		kmem_free(sr->sr_request_buf, sr->sr_req_length);
1156*faa1795aSjb 	if (sr->command.chain)
1157*faa1795aSjb 		m_freem(sr->command.chain);
1158*faa1795aSjb 	if (sr->reply.chain)
1159*faa1795aSjb 		m_freem(sr->reply.chain);
1160*faa1795aSjb 	if (sr->raw_data.chain)
1161*faa1795aSjb 		m_freem(sr->raw_data.chain);
1162*faa1795aSjb 
1163*faa1795aSjb 	sr->sr_magic = 0;
1164*faa1795aSjb 	mutex_destroy(&sr->sr_mutex);
1165*faa1795aSjb 	kmem_cache_free(sr->sr_cache, sr);
1166da6c28aaSamw }
1167