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