summaryrefslogtreecommitdiffstats
path: root/sys/netsmb
diff options
context:
space:
mode:
authorbp <bp@FreeBSD.org>2001-04-10 07:59:06 +0000
committerbp <bp@FreeBSD.org>2001-04-10 07:59:06 +0000
commita414f03f5d87ade219aa2e4bcd0830eceaee6bd3 (patch)
treee02d13218bf41d783a93777341cba62b2921dc10 /sys/netsmb
parent88436d21df771b1b6d764b003c5daecf40dd767f (diff)
downloadFreeBSD-src-a414f03f5d87ade219aa2e4bcd0830eceaee6bd3.zip
FreeBSD-src-a414f03f5d87ade219aa2e4bcd0830eceaee6bd3.tar.gz
Import kernel part of SMB/CIFS requester.
Add smbfs(CIFS) filesystem. Userland part will be in the ports tree for a while. Obtained from: smbfs-1.3.7-dev package.
Diffstat (limited to 'sys/netsmb')
-rw-r--r--sys/netsmb/netbios.h138
-rw-r--r--sys/netsmb/smb.h388
-rw-r--r--sys/netsmb/smb_conn.c874
-rw-r--r--sys/netsmb/smb_conn.h464
-rw-r--r--sys/netsmb/smb_crypt.c146
-rw-r--r--sys/netsmb/smb_dev.c448
-rw-r--r--sys/netsmb/smb_dev.h199
-rw-r--r--sys/netsmb/smb_iod.c709
-rw-r--r--sys/netsmb/smb_rq.c752
-rw-r--r--sys/netsmb/smb_rq.h151
-rw-r--r--sys/netsmb/smb_smb.c660
-rw-r--r--sys/netsmb/smb_subr.c359
-rw-r--r--sys/netsmb/smb_subr.h198
-rw-r--r--sys/netsmb/smb_tran.h89
-rw-r--r--sys/netsmb/smb_trantcp.c672
-rw-r--r--sys/netsmb/smb_trantcp.h88
-rw-r--r--sys/netsmb/smb_usr.c355
17 files changed, 6690 insertions, 0 deletions
diff --git a/sys/netsmb/netbios.h b/sys/netsmb/netbios.h
new file mode 100644
index 0000000..961f5be
--- /dev/null
+++ b/sys/netsmb/netbios.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NETSMB_NETBIOS_H_
+#define _NETSMB_NETBIOS_H_
+
+/*
+ * make this file dirty...
+ */
+#ifndef _NETINET_IN_H_
+#include <netinet/in.h>
+#endif
+
+#ifndef _NETIPX_IPX_H_
+#include <netipx/ipx.h>
+#endif
+
+#define AF_NETBIOS AF_NS /* XXX: should go to socket.h */
+#define PF_NETBIOS AF_NETBIOS
+
+#define NBPROTO_TCPSSN 1 /* NETBIOS session over TCP */
+#define NBPROTO_IPXSSN 11 /* NETBIOS over IPX */
+
+#define NB_NAMELEN 16
+#define NB_ENCNAMELEN NB_NAMELEN * 2
+#define NB_MAXLABLEN 63
+
+#define NB_MINSALEN (sizeof(struct sockaddr_nb))
+
+/*
+ * name types
+ */
+#define NBT_WKSTA 0x00
+#define NBT_SERVER 0x20
+
+/*
+ * Session packet types
+ */
+#define NB_SSN_MESSAGE 0x0
+#define NB_SSN_REQUEST 0x81
+#define NB_SSN_POSRESP 0x82
+#define NB_SSN_NEGRESP 0x83
+#define NB_SSN_RTGRESP 0x84
+#define NB_SSN_KEEPALIVE 0x85
+
+/*
+ * resolver: Opcodes
+ */
+#define NBNS_OPCODE_QUERY 0x00
+#define NBNS_OPCODE_REGISTER 0x05
+#define NBNS_OPCODE_RELEASE 0x06
+#define NBNS_OPCODE_WACK 0x07
+#define NBNS_OPCODE_REFRESH 0x08
+#define NBNS_OPCODE_RESPONSE 0x10 /* or'ed with other opcodes */
+
+/*
+ * resolver: NM_FLAGS
+ */
+#define NBNS_NMFLAG_BCAST 0x01
+#define NBNS_NMFLAG_RA 0x08 /* recursion available */
+#define NBNS_NMFLAG_RD 0x10 /* recursion desired */
+#define NBNS_NMFLAG_TC 0x20 /* truncation occured */
+#define NBNS_NMFLAG_AA 0x40 /* authoritative answer */
+
+/*
+ * resolver: Question types
+ */
+#define NBNS_QUESTION_TYPE_NB 0x0020
+#define NBNS_QUESTION_TYPE_NBSTAT 0x0021
+
+/*
+ * resolver: Question class
+ */
+#define NBNS_QUESTION_CLASS_IN 0x0001
+
+/*
+ * resolver: Limits
+ */
+#define NBNS_MAXREDIRECTS 3 /* maximum number of accepted redirects */
+#define NBDG_MAXSIZE 576 /* maximum nbns datagram size */
+
+/*
+ * NETBIOS addressing
+ */
+union nb_tran {
+ struct sockaddr_in x_in;
+ struct sockaddr_ipx x_ipx;
+};
+
+struct nb_name {
+ u_int nn_type;
+ u_char nn_name[NB_NAMELEN + 1];
+ u_char * nn_scope;
+};
+
+/*
+ * Socket address
+ */
+struct sockaddr_nb {
+ u_char snb_len;
+ u_char snb_family;
+ union nb_tran snb_tran; /* transport */
+ u_char snb_name[1 + NB_ENCNAMELEN + 1]; /* encoded */
+};
+
+#define snb_addrin snb_tran.x_in
+
+#endif /* !_NETSMB_NETBIOS_H_ */
diff --git a/sys/netsmb/smb.h b/sys/netsmb/smb.h
new file mode 100644
index 0000000..4bd3096
--- /dev/null
+++ b/sys/netsmb/smb.h
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Common definintions and structures for SMB/CIFS protocol
+ */
+
+#ifndef _NETSMB_SMB_H_
+#define _NETSMB_SMB_H_
+
+#define SMB_TCP_PORT 139
+/*
+ * SMB dialects that we have to deal with.
+ */
+enum smb_dialects {
+ SMB_DIALECT_NONE,
+ SMB_DIALECT_CORE, /* PC NETWORK PROGRAM 1.0, PCLAN1.0 */
+ SMB_DIALECT_COREPLUS, /* MICROSOFT NETWORKS 1.03 */
+ SMB_DIALECT_LANMAN1_0, /* MICROSOFT NETWORKS 3.0, LANMAN1.0 */
+ SMB_DIALECT_LANMAN2_0, /* LM1.2X002, DOS LM1.2X002, Samba */
+ SMB_DIALECT_LANMAN2_1, /* DOS LANMAN2.1, LANMAN2.1 */
+ SMB_DIALECT_NTLM0_12 /* NT LM 0.12, Windows for Workgroups 3.1a,
+ * NT LANMAN 1.0 */
+};
+
+/*
+ * Formats of data/string buffers
+ */
+#define SMB_DT_DATA 1
+#define SMB_DT_DIALECT 2
+#define SMB_DT_PATHNAME 3
+#define SMB_DT_ASCII 4
+#define SMB_DT_VARIABLE 5
+
+/*
+ * SMB header
+ */
+#define SMB_SIGNATURE "\xFFSMB"
+#define SMB_SIGLEN 4
+#define SMB_HDRMID(p) (*(u_short*)((u_char*)(p) + 30))
+#define SMB_HDRLEN 32
+/*
+ * bits in the smb_flags field
+ */
+#define SMB_FLAGS_CASELESS 0x08
+#define SMB_FLAGS_SERVER_RESP 0x80 /* indicates a response */
+
+/*
+ * bits in the smb_flags2 field
+ */
+#define SMB_FLAGS2_KNOWS_LONG_NAMES 0x0001
+#define SMB_FLAGS2_KNOWS_EAS 0x0002 /* client know about EAs */
+#define SMB_FLAGS2_SECURITY_SIGNATURE 0x0004 /* check SMB integrity */
+#define SMB_FLAGS2_IS_LONG_NAME 0x0040 /* any path name is a long name */
+#define SMB_FLAGS2_EXT_SEC 0x0800 /* client aware of Extended
+ * Security negotiation */
+#define SMB_FLAGS2_DFS 0x1000 /* resolve paths in DFS */
+#define SMB_FLAGS2_PAGING_IO 0x2000 /* for exec */
+#define SMB_FLAGS2_ERR_STATUS 0x4000 /* 1 - status.status */
+#define SMB_FLAGS2_UNICODE 0x8000 /* use Unicode for all strings */
+
+#define SMB_UID_UNKNOWN 0xffff
+#define SMB_TID_UNKNOWN 0xffff
+
+/*
+ * Security mode bits
+ */
+#define SMB_SM_USER 0x01 /* server in the user security mode */
+#define SMB_SM_ENCRYPT 0x02 /* use challenge/responce */
+
+/*
+ * NTLM capabilities
+ */
+#define SMB_CAP_RAW_MODE 0x0001
+#define SMB_CAP_MPX_MODE 0x0002
+#define SMB_CAP_UNICODE 0x0004
+#define SMB_CAP_LARGE_FILES 0x0008 /* 64 bit offsets supported */
+#define SMB_CAP_NT_SMBS 0x0010
+#define SMB_CAP_NT_FIND 0x0200
+#define SMB_CAP_EXT_SECURITY 0x80000000
+
+/*
+ * File attributes
+ */
+#define SMB_FA_RDONLY 0x01
+#define SMB_FA_HIDDEN 0x02
+#define SMB_FA_SYSTEM 0x04
+#define SMB_FA_VOLUME 0x08
+#define SMB_FA_DIR 0x10
+#define SMB_FA_ARCHIVE 0x20
+
+/*
+ * Extended file attributes
+ */
+#define SMB_EFA_RDONLY 0x0001
+#define SMB_EFA_HIDDEN 0x0002
+#define SMB_EFA_SYSTEM 0x0004
+#define SMB_EFA_ARCHIVE 0x0020
+#define SMB_EFA_NORMAL 0x0080
+#define SMB_EFA_TEMPORARY 0x0100
+#define SMB_EFA_COMPRESSED 0x0800
+#define SMB_EFA_POSIX_SEMANTICS 0x00100000
+#define SMB_EFA_BACKUP_SEMANTICS 0x02000000
+#define SMB_EFA_DELETE_ON_CLOSE 0x04000000
+#define SMB_EFA_SEQUENTIAL_SCAN 0x08000000
+#define SMB_EFA_RANDOM_ACCESS 0x10000000
+#define SMB_EFA_NO_BUFFERING 0x20000000
+#define SMB_EFA_WRITE_THROUGH 0x80000000
+
+/*
+ * Access Mode Encoding
+ */
+#define SMB_AM_OPENREAD 0x0000
+#define SMB_AM_OPENWRITE 0x0001
+#define SMB_AM_OPENRW 0x0002
+#define SMB_AM_OPENEXEC 0x0003
+#define SMB_SM_COMPAT 0x0000
+#define SMB_SM_EXCLUSIVE 0x0010
+#define SMB_SM_DENYWRITE 0x0020
+#define SMB_SM_DENYREADEXEC 0x0030
+#define SMB_SM_DENYNONE 0x0040
+
+/*
+ * SMB commands
+ */
+#define SMB_COM_CREATE_DIRECTORY 0x00
+#define SMB_COM_DELETE_DIRECTORY 0x01
+#define SMB_COM_OPEN 0x02
+#define SMB_COM_CREATE 0x03
+#define SMB_COM_CLOSE 0x04
+#define SMB_COM_FLUSH 0x05
+#define SMB_COM_DELETE 0x06
+#define SMB_COM_RENAME 0x07
+#define SMB_COM_QUERY_INFORMATION 0x08
+#define SMB_COM_SET_INFORMATION 0x09
+#define SMB_COM_READ 0x0A
+#define SMB_COM_WRITE 0x0B
+#define SMB_COM_LOCK_BYTE_RANGE 0x0C
+#define SMB_COM_UNLOCK_BYTE_RANGE 0x0D
+#define SMB_COM_CREATE_TEMPORARY 0x0E
+#define SMB_COM_CREATE_NEW 0x0F
+#define SMB_COM_CHECK_DIRECTORY 0x10
+#define SMB_COM_PROCESS_EXIT 0x11
+#define SMB_COM_SEEK 0x12
+#define SMB_COM_LOCK_AND_READ 0x13
+#define SMB_COM_WRITE_AND_UNLOCK 0x14
+#define SMB_COM_READ_RAW 0x1A
+#define SMB_COM_READ_MPX 0x1B
+#define SMB_COM_READ_MPX_SECONDARY 0x1C
+#define SMB_COM_WRITE_RAW 0x1D
+#define SMB_COM_WRITE_MPX 0x1E
+#define SMB_COM_WRITE_COMPLETE 0x20
+#define SMB_COM_SET_INFORMATION2 0x22
+#define SMB_COM_QUERY_INFORMATION2 0x23
+#define SMB_COM_LOCKING_ANDX 0x24
+#define SMB_COM_TRANSACTION 0x25
+#define SMB_COM_TRANSACTION_SECONDARY 0x26
+#define SMB_COM_IOCTL 0x27
+#define SMB_COM_IOCTL_SECONDARY 0x28
+#define SMB_COM_COPY 0x29
+#define SMB_COM_MOVE 0x2A
+#define SMB_COM_ECHO 0x2B
+#define SMB_COM_WRITE_AND_CLOSE 0x2C
+#define SMB_COM_OPEN_ANDX 0x2D
+#define SMB_COM_READ_ANDX 0x2E
+#define SMB_COM_WRITE_ANDX 0x2F
+#define SMB_COM_CLOSE_AND_TREE_DISC 0x31
+#define SMB_COM_TRANSACTION2 0x32
+#define SMB_COM_TRANSACTION2_SECONDARY 0x33
+#define SMB_COM_FIND_CLOSE2 0x34
+#define SMB_COM_FIND_NOTIFY_CLOSE 0x35
+#define SMB_COM_TREE_CONNECT 0x70
+#define SMB_COM_TREE_DISCONNECT 0x71
+#define SMB_COM_NEGOTIATE 0x72
+#define SMB_COM_SESSION_SETUP_ANDX 0x73
+#define SMB_COM_LOGOFF_ANDX 0x74
+#define SMB_COM_TREE_CONNECT_ANDX 0x75
+#define SMB_COM_QUERY_INFORMATION_DISK 0x80
+#define SMB_COM_SEARCH 0x81
+#define SMB_COM_FIND 0x82
+#define SMB_COM_FIND_UNIQUE 0x83
+#define SMB_COM_NT_TRANSACT 0xA0
+#define SMB_COM_NT_TRANSACT_SECONDARY 0xA1
+#define SMB_COM_NT_CREATE_ANDX 0xA2
+#define SMB_COM_NT_CANCEL 0xA4
+#define SMB_COM_OPEN_PRINT_FILE 0xC0
+#define SMB_COM_WRITE_PRINT_FILE 0xC1
+#define SMB_COM_CLOSE_PRINT_FILE 0xC2
+#define SMB_COM_GET_PRINT_QUEUE 0xC3
+#define SMB_COM_READ_BULK 0xD8
+#define SMB_COM_WRITE_BULK 0xD9
+#define SMB_COM_WRITE_BULK_DATA 0xDA
+
+/*
+ * TRANS2 commands
+ */
+#define SMB_TRANS2_OPEN2 0x00
+#define SMB_TRANS2_FIND_FIRST2 0x01
+#define SMB_TRANS2_FIND_NEXT2 0x02
+#define SMB_TRANS2_QUERY_FS_INFORMATION 0x03
+#define SMB_TRANS2_QUERY_PATH_INFORMATION 0x05
+#define SMB_TRANS2_SET_PATH_INFORMATION 0x06
+#define SMB_TRANS2_QUERY_FILE_INFORMATION 0x07
+#define SMB_TRANS2_SET_FILE_INFORMATION 0x08
+#define SMB_TRANS2_FSCTL 0x09
+#define SMB_TRANS2_IOCTL2 0x0A
+#define SMB_TRANS2_FIND_NOTIFY_FIRST 0x0B
+#define SMB_TRANS2_FIND_NOTIFY_NEXT 0x0C
+#define SMB_TRANS2_CREATE_DIRECTORY 0x0D
+#define SMB_TRANS2_SESSION_SETUP 0x0E
+#define SMB_TRANS2_GET_DFS_REFERRAL 0x10
+#define SMB_TRANS2_REPORT_DFS_INCONSISTENCY 0x11
+
+/*
+ * SMB_TRANS2_QUERY_FS_INFORMATION levels
+ */
+#define SMB_INFO_ALLOCATION 1
+#define SMB_INFO_VOLUME 2
+#define SMB_QUERY_FS_VOLUME_INFO 0x102
+#define SMB_QUERY_FS_SIZE_INFO 0x103
+#define SMB_QUERY_FS_DEVICE_INFO 0x104
+#define SMB_QUERY_FS_ATTRIBUTE_INFO 0x105
+
+/*
+ * SMB_TRANS2_FIND_FIRST2 information levels
+ */
+#define SMB_INFO_STANDARD 1
+#define SMB_INFO_QUERY_EA_SIZE 2
+#define SMB_INFO_QUERY_EAS_FROM_LIST 3
+#define SMB_FIND_FILE_DIRECTORY_INFO 0x101
+#define SMB_FIND_FULL_DIRECTORY_INFO 0x102
+#define SMB_FIND_FILE_NAMES_INFO 0x103
+#define SMB_FIND_BOTH_DIRECTORY_INFO 0x104
+
+/*
+ * Set PATH/FILE information levels
+ */
+#define SMB_SET_FILE_BASIC_INFO 0x101
+
+/*
+ * LOCKING_ANDX LockType flags
+ */
+#define SMB_LOCKING_ANDX_SHARED_LOCK 0x01
+#define SMB_LOCKING_ANDX_OPLOCK_RELEASE 0x02
+#define SMB_LOCKING_ANDX_CHANGE_LOCKTYPE 0x04
+#define SMB_LOCKING_ANDX_CANCEL_LOCK 0x08
+#define SMB_LOCKING_ANDX_LARGE_FILES 0x10
+
+/*
+ * Some names length limitations. Some of them aren't declared by specs,
+ * but we need reasonable limits.
+ */
+#define SMB_MAXSRVNAMELEN 15 /* NetBIOS limit */
+#define SMB_MAXUSERNAMELEN 128
+#define SMB_MAXPASSWORDLEN 128
+#define SMB_MAXSHARENAMELEN 128
+#define SMB_MAXPKTLEN 0x1FFFF
+#define SMB_MAXCHALLENGELEN 8
+#define SMB_MAXFNAMELEN 255 /* Keep in sync with MAXNAMLEN */
+
+#define SMB_MAXRCN 3 /* number of reconnect attempts */
+
+/*
+ * Error classes
+ */
+#define SMBSUCCESS 0x00
+#define ERRDOS 0x01
+#define ERRSRV 0x02
+#define ERRHRD 0x03 /* Error is an hardware error. */
+#define ERRCMD 0xFF /* Command was not in the "SMB" format. */
+
+/*
+ * Error codes for the ERRDOS class
+ */
+#define ERRbadfunc 1 /* Invalid function */
+#define ERRbadfile 2 /* File not found (last component) */
+#define ERRbadpath 3 /* Directory invalid */
+#define ERRnofids 4 /* Too many open files */
+#define ERRnoaccess 5 /* Access denied */
+#define ERRbadfid 6 /* Invalid file handle */
+#define ERRbadmcb 7 /* Memory control blocks destroyed (huh ?) */
+#define ERRnomem 8 /* Insufficient memory */
+#define ERRbadmem 9 /* Invalid memory block address */
+#define ERRbadenv 10 /* Invalid environment */
+#define ERRbadformat 11 /* Invalid format */
+#define ERRbadaccess 12 /* Invalid open mode */
+#define ERRbaddata 13 /* Invalid data */
+#define ERRbaddrive 15 /* Invalid drive specified */
+#define ERRremcd 16 /* An attempt to delete current directory */
+#define ERRdiffdevice 17 /* cross fs rename/move */
+#define ERRnofiles 18 /* no more files found in file search */
+#define ERRbadshare 32 /* Share mode can't be granted */
+#define ERRlock 33 /* A lock request conflicts with existing lock */
+#define ERRfilexists 80 /* The file named in the request already exists */
+
+/*
+ * Error codes for the ERRSRV class
+ */
+#define ERRerror 1 /* Non-specific error code */
+#define ERRbadpw 2 /* Bad password */
+#define ERRaccess 4 /* The client doesn't have enough access rights */
+#define ERRinvnid 5 /* The Tid specified in a command is invalid */
+#define ERRinvnetname 6 /* Invalid server name in the tree connect */
+#define ERRinvdevice 7 /* Printer and not printer devices are mixed */
+#define ERRqfull 49 /* Print queue full */
+#define ERRqtoobig 50 /* Print queue full - no space */
+#define ERRinvpfid 52 /* Invalid print file FID */
+#define ERRsmbcmd 64 /* The server did not recognise the command */
+#define ERRsrverror 65 /* The server encountered and internal error */
+#define ERRfilespecs 67 /* The Fid and path name contains an invalid combination */
+#define ERRbadpermits 69 /* Access mode invalid */
+#define ERRsetattrmode 71 /* Attribute mode invalid */
+#define ERRpaused 81 /* Server is paused */
+#define ERRmsgoff 82 /* Not receiving messages */
+#define ERRnoroom 83 /* No room to buffer message */
+#define ERRrmuns 87 /* Too many remote user names */
+#define ERRtimeout 88 /* Operation timed out */
+#define ERRnoresource 89 /* No resources currently available for request */
+#define ERRtoomanyuids 90 /* Too many UIDs active on this session */
+#define ERRbaduid 91 /* The UID is not known in this session */
+#define ERRusempx 250 /* Temporarily unable to support Raw, use MPX mode */
+#define ERRusestd 251 /* Temporarily unable to support Raw, use stdandard r/w */
+#define ERRcontmpx 252 /* Continue in MPX mode */
+#define ERRnosupport 65535 /* Invalid function */
+
+/*
+ * Error codes for the ERRHRD class
+ */
+#define ERRnowrite 19 /* write protected media */
+#define ERRbadunit 20 /* Unknown unit */
+#define ERRnotready 21 /* Drive not ready */
+#define ERRbadcmd 22 /* Unknown command */
+#define ERRdata 23 /* Data error (CRC) */
+#define ERRbadreq 24 /* Bad request structure length */
+#define ERRseek 25 /* Seek error */
+#define ERRbadmedia 26 /* Unknown media type */
+#define ERRbadsector 27 /* Sector not found */
+#define ERRnopaper 28 /* Printer out of paper */
+#define ERRwrite 29 /* Write fault */
+#define ERRread 30 /* Read fault */
+#define ERRgeneral 31 /* General failure */
+#define ERRbadshare 32 /* A open conflicts with an existing open */
+#define ERRlock 33 /* lock/unlock conflict */
+#define ERRwrongdisk 34 /* The wrong disk was found in a drive */
+#define ERRFCBunavail 35 /* No FCBs available */
+#define ERRsharebufexc 36 /* A sharing buffer has been exceeded */
+
+/*
+ * RAP error codes (it seems that they returned not only by RAP)
+ */
+#define SMB_ERROR_ACCESS_DENIED 5
+#define SMB_ERROR_NETWORK_ACCESS_DENIED 65
+#define SMB_ERROR_MORE_DATA 234
+
+typedef u_int16_t smbfh;
+
+#endif /* _NETSMB_SMB_H_ */
diff --git a/sys/netsmb/smb_conn.c b/sys/netsmb/smb_conn.c
new file mode 100644
index 0000000..54f5c0b
--- /dev/null
+++ b/sys/netsmb/smb_conn.c
@@ -0,0 +1,874 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Connection engine.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/sysctl.h>
+#include <sys/socketvar.h>
+
+#include <sys/iconv.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_tran.h>
+#include <netsmb/smb_trantcp.h>
+
+static struct smb_connobj smb_vclist;
+static int smb_vcnext = 1; /* next unique id for VC */
+
+extern struct linker_set sysctl_net_smb;
+
+SYSCTL_NODE(_net, OID_AUTO, smb, CTLFLAG_RW, NULL, "SMB protocol");
+
+MALLOC_DEFINE(M_SMBCONN, "SMB conn", "SMB connection");
+
+static void smb_co_init(struct smb_connobj *cp, int level, char *objname,
+ struct proc *p);
+static void smb_co_done(struct smb_connobj *cp);
+static int smb_co_lockstatus(struct smb_connobj *cp, struct proc *p);
+
+static int smb_vc_disconnect(struct smb_vc *vcp);
+static void smb_vc_free(struct smb_connobj *cp);
+static void smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred);
+static smb_co_free_t smb_share_free;
+static smb_co_gone_t smb_share_gone;
+
+static int smb_sysctl_treedump(SYSCTL_HANDLER_ARGS);
+
+SYSCTL_PROC(_net_smb, OID_AUTO, treedump, CTLFLAG_RD | CTLTYPE_OPAQUE,
+ NULL, 0, smb_sysctl_treedump, "S,treedump", "Requester tree");
+
+int
+smb_sm_init(void)
+{
+
+ smb_co_init(&smb_vclist, SMBL_SM, "smbsm", curproc);
+ smb_co_unlock(&smb_vclist, 0, curproc);
+ return 0;
+}
+
+int
+smb_sm_done(void)
+{
+
+ /* XXX: hold the mutex */
+ if (smb_vclist.co_usecount > 1) {
+ SMBERROR("%d connections still active\n", smb_vclist.co_usecount - 1);
+ return EBUSY;
+ }
+ smb_co_done(&smb_vclist);
+ return 0;
+}
+
+static int
+smb_sm_lockvclist(int flags, struct proc *p)
+{
+
+ return smb_co_lock(&smb_vclist, flags | LK_CANRECURSE, p);
+}
+
+static void
+smb_sm_unlockvclist(struct proc *p)
+{
+
+ smb_co_unlock(&smb_vclist, LK_RELEASE, p);
+}
+
+static int
+smb_sm_lookupint(struct smb_vcspec *vcspec, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_vc **vcpp)
+{
+ struct proc *p = scred->scr_p;
+ struct smb_vc *vcp;
+ int exact = 1;
+ int error;
+
+ vcspec->shspec = shspec;
+ error = ENOENT;
+ SMBCO_FOREACH((struct smb_connobj*)vcp, &smb_vclist) {
+ error = smb_vc_lock(vcp, LK_EXCLUSIVE, p);
+ if (error)
+ continue;
+ itry {
+ if ((vcp->obj.co_flags & SMBV_PRIVATE) ||
+ !CONNADDREQ(vcp->vc_paddr, vcspec->sap) ||
+ strcmp(vcp->vc_username, vcspec->username) != 0)
+ ithrow(1);
+ if (vcspec->owner != SMBM_ANY_OWNER) {
+ if (vcp->vc_uid != vcspec->owner)
+ ithrow(1);
+ } else
+ exact = 0;
+ if (vcspec->group != SMBM_ANY_GROUP) {
+ if (vcp->vc_grp != vcspec->group)
+ ithrow(1);
+ } else
+ exact = 0;
+
+ if (vcspec->mode & SMBM_EXACT) {
+ if (!exact ||
+ (vcspec->mode & SMBM_MASK) != vcp->vc_mode)
+ ithrow(1);
+ }
+ if (smb_vc_access(vcp, scred, vcspec->mode) != 0)
+ ithrow(1);
+ vcspec->ssp = NULL;
+ if (shspec)
+ ithrow(smb_vc_lookupshare(vcp, shspec, scred, &vcspec->ssp));
+ error = 0;
+ break;
+ } icatch(error) {
+ smb_vc_unlock(vcp, 0, p);
+ } ifinally {
+ } iendtry;
+ if (error == 0)
+ break;
+ }
+ if (vcp) {
+ smb_vc_ref(vcp, p);
+ *vcpp = vcp;
+ }
+ return error;
+}
+
+int
+smb_sm_lookup(struct smb_vcspec *vcspec, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_vc **vcpp)
+{
+ struct proc *p = scred->scr_p;
+ struct smb_vc *vcp;
+ struct smb_share *ssp = NULL;
+ int error;
+
+ *vcpp = vcp = NULL;
+
+ error = smb_sm_lockvclist(LK_EXCLUSIVE, p);
+ if (error)
+ return error;
+ error = smb_sm_lookupint(vcspec, shspec, scred, vcpp);
+ if (error == 0 || (vcspec->flags & SMBV_CREATE) == 0) {
+ smb_sm_unlockvclist(p);
+ return error;
+ }
+ error = smb_sm_lookupint(vcspec, NULL, scred, &vcp);
+ if (error) {
+ error = smb_vc_create(vcspec, scred, &vcp);
+ if (error)
+ goto out;
+ error = smb_vc_connect(vcp, scred);
+ if (error)
+ goto out;
+ }
+ if (shspec == NULL)
+ goto out;
+ error = smb_share_create(vcp, shspec, scred, &ssp);
+ if (error)
+ goto out;
+ error = smb_smb_treeconnect(ssp, scred);
+ if (error == 0)
+ vcspec->ssp = ssp;
+ else
+ smb_share_put(ssp, scred);
+out:
+ smb_sm_unlockvclist(p);
+ if (error == 0)
+ *vcpp = vcp;
+ else if (vcp)
+ smb_vc_put(vcp, scred);
+ return error;
+}
+
+/*
+ * Common code for connection object
+ */
+static void
+smb_co_init(struct smb_connobj *cp, int level, char *objname, struct proc *p)
+{
+ SLIST_INIT(&cp->co_children);
+ smb_sl_init(&cp->co_interlock, objname);
+ lockinit(&cp->co_lock, PZERO, objname, 0, 0);
+ cp->co_level = level;
+ cp->co_usecount = 1;
+ KASSERT(smb_co_lock(cp, LK_EXCLUSIVE, p) == 0, ("smb_co_init: lock failed"));
+}
+
+static void
+smb_co_done(struct smb_connobj *cp)
+{
+ smb_sl_destroy(&cp->co_interlock);
+ lockdestroy(&cp->co_lock);
+}
+
+static void
+smb_co_gone(struct smb_connobj *cp, struct smb_cred *scred)
+{
+ struct smb_connobj *parent;
+
+ if (cp->co_gone)
+ cp->co_gone(cp, scred);
+ parent = cp->co_parent;
+ if (parent) {
+ smb_co_lock(parent, LK_EXCLUSIVE, scred->scr_p);
+ SLIST_REMOVE(&parent->co_children, cp, smb_connobj, co_next);
+ smb_co_put(parent, scred);
+ }
+ if (cp->co_free)
+ cp->co_free(cp);
+}
+
+void
+smb_co_ref(struct smb_connobj *cp, struct proc *p)
+{
+
+ SMB_CO_LOCK(cp);
+ cp->co_usecount++;
+ SMB_CO_UNLOCK(cp);
+}
+
+void
+smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred)
+{
+ struct proc *p = scred->scr_p;
+
+ SMB_CO_LOCK(cp);
+ if (cp->co_usecount > 1) {
+ cp->co_usecount--;
+ SMB_CO_UNLOCK(cp);
+ return;
+ }
+ if (cp->co_usecount == 0) {
+ SMBERROR("negative use_count for object %d", cp->co_level);
+ SMB_CO_UNLOCK(cp);
+ return;
+ }
+ cp->co_usecount--;
+ cp->co_flags |= SMBO_GONE;
+
+ lockmgr(&cp->co_lock, LK_DRAIN | LK_INTERLOCK, &cp->co_interlock, p);
+ smb_co_gone(cp, scred);
+}
+
+int
+smb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred)
+{
+ int error;
+
+ if ((flags & LK_INTERLOCK) == 0)
+ SMB_CO_LOCK(cp);
+ cp->co_usecount++;
+ error = smb_co_lock(cp, flags | LK_INTERLOCK, scred->scr_p);
+ if (error) {
+ SMB_CO_LOCK(cp);
+ cp->co_usecount--;
+ SMB_CO_UNLOCK(cp);
+ return error;
+ }
+ return 0;
+}
+
+void
+smb_co_put(struct smb_connobj *cp, struct smb_cred *scred)
+{
+ struct proc *p = scred->scr_p;
+ int flags;
+
+ flags = LK_RELEASE;
+ SMB_CO_LOCK(cp);
+ if (cp->co_usecount > 1) {
+ cp->co_usecount--;
+ } else if (cp->co_usecount == 1) {
+ cp->co_usecount--;
+ cp->co_flags |= SMBO_GONE;
+ flags = LK_DRAIN;
+ } else {
+ SMBERROR("negative usecount");
+ }
+ lockmgr(&cp->co_lock, LK_RELEASE | LK_INTERLOCK, &cp->co_interlock, p);
+ if ((cp->co_flags & SMBO_GONE) == 0)
+ return;
+ lockmgr(&cp->co_lock, LK_DRAIN, NULL, p);
+ smb_co_gone(cp, scred);
+}
+
+int
+smb_co_lockstatus(struct smb_connobj *cp, struct proc *p)
+{
+ return lockstatus(&cp->co_lock, p);
+}
+
+int
+smb_co_lock(struct smb_connobj *cp, int flags, struct proc *p)
+{
+
+ if (cp->co_flags & SMBO_GONE)
+ return EINVAL;
+ if ((flags & LK_TYPE_MASK) == 0)
+ flags |= LK_EXCLUSIVE;
+ if (smb_co_lockstatus(cp, p) == LK_EXCLUSIVE &&
+ (flags & LK_CANRECURSE) == 0) {
+ SMBERROR("recursive lock for object %d\n", cp->co_level);
+ return 0;
+ }
+ return lockmgr(&cp->co_lock, flags, &cp->co_interlock, p);
+}
+
+void
+smb_co_unlock(struct smb_connobj *cp, int flags, struct proc *p)
+{
+ (void)lockmgr(&cp->co_lock, flags | LK_RELEASE, &cp->co_interlock, p);
+}
+
+static void
+smb_co_addchild(struct smb_connobj *parent, struct smb_connobj *child)
+{
+ KASSERT(smb_co_lockstatus(parent, curproc) == LK_EXCLUSIVE, ("smb_co_addchild: parent not locked"));
+ KASSERT(smb_co_lockstatus(child, curproc) == LK_EXCLUSIVE, ("smb_co_addchild: child not locked"));
+
+ smb_co_ref(parent, curproc);
+ SLIST_INSERT_HEAD(&parent->co_children, child, co_next);
+ child->co_parent = parent;
+}
+
+/*
+ * Session implementation
+ */
+
+int
+smb_vc_create(struct smb_vcspec *vcspec,
+ struct smb_cred *scred, struct smb_vc **vcpp)
+{
+ struct smb_vc *vcp;
+ struct proc *p = scred->scr_p;
+ struct ucred *cred = scred->scr_cred;
+ uid_t uid = vcspec->owner;
+ gid_t gid = vcspec->group;
+ uid_t realuid = cred->cr_uid;
+ char *domain = vcspec->domain;
+ int error, isroot;
+
+ isroot = smb_suser(cred) == 0;
+ /*
+ * Only superuser can create VCs with different uid and gid
+ */
+ if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot)
+ return EPERM;
+ if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot)
+ return EPERM;
+
+ vcp = smb_zmalloc(sizeof(*vcp), M_SMBCONN, M_WAITOK);
+ smb_co_init(VCTOCP(vcp), SMBL_VC, "smb_vc", p);
+ vcp->obj.co_free = smb_vc_free;
+ vcp->obj.co_gone = smb_vc_gone;
+ vcp->vc_number = smb_vcnext++;
+ vcp->vc_timo = SMB_DEFRQTIMO;
+ vcp->vc_smbuid = SMB_UID_UNKNOWN;
+ vcp->vc_mode = vcspec->rights & SMBM_MASK;
+ vcp->obj.co_flags = vcspec->flags & (SMBV_PRIVATE | SMBV_SINGLESHARE);
+ vcp->vc_tdesc = &smb_tran_nbtcp_desc;
+
+ if (uid == SMBM_ANY_OWNER)
+ uid = realuid;
+ if (gid == SMBM_ANY_GROUP)
+ gid = cred->cr_groups[0];
+ vcp->vc_uid = uid;
+ vcp->vc_grp = gid;
+
+ smb_sl_init(&vcp->vc_stlock, "vcstlock");
+ error = 0;
+ itry {
+ vcp->vc_paddr = dup_sockaddr(vcspec->sap, 1);
+ ierror(vcp->vc_paddr == NULL, ENOMEM);
+
+ vcp->vc_laddr = dup_sockaddr(vcspec->lap, 1);
+ ierror(vcp->vc_laddr == NULL, ENOMEM);
+
+ ierror((vcp->vc_pass = smb_strdup(vcspec->pass)) == NULL, ENOMEM);
+
+ vcp->vc_domain = smb_strdup((domain && domain[0]) ? domain : "NODOMAIN");
+ ierror(vcp->vc_domain == NULL, ENOMEM);
+
+ ierror((vcp->vc_srvname = smb_strdup(vcspec->srvname)) == NULL, ENOMEM);
+ ierror((vcp->vc_username = smb_strdup(vcspec->username)) == NULL, ENOMEM);
+
+ ithrow(iconv_open("tolower", vcspec->localcs, &vcp->vc_tolower));
+ ithrow(iconv_open("toupper", vcspec->localcs, &vcp->vc_toupper));
+ if (vcspec->servercs[0]) {
+ ithrow(iconv_open(vcspec->servercs, vcspec->localcs,
+ &vcp->vc_toserver));
+ ithrow(iconv_open(vcspec->localcs, vcspec->servercs,
+ &vcp->vc_tolocal));
+ }
+
+ ithrow(smb_iod_create(vcp));
+ *vcpp = vcp;
+ smb_co_addchild(&smb_vclist, VCTOCP(vcp));
+ } icatch(error) {
+ smb_vc_put(vcp, scred);
+ } ifinally {
+ } iendtry;
+ return error;
+}
+
+static void
+smb_vc_free(struct smb_connobj *cp)
+{
+ struct smb_vc *vcp = CPTOVC(cp);
+
+ if (vcp->vc_iod)
+ smb_iod_destroy(vcp->vc_iod);
+ SMB_STRFREE(vcp->vc_username);
+ SMB_STRFREE(vcp->vc_srvname);
+ SMB_STRFREE(vcp->vc_pass);
+ SMB_STRFREE(vcp->vc_domain);
+ if (vcp->vc_paddr)
+ free(vcp->vc_paddr, M_SONAME);
+ if (vcp->vc_laddr)
+ free(vcp->vc_laddr, M_SONAME);
+ if (vcp->vc_tolower)
+ iconv_close(vcp->vc_tolower);
+ if (vcp->vc_toupper)
+ iconv_close(vcp->vc_toupper);
+ if (vcp->vc_tolocal)
+ iconv_close(vcp->vc_tolocal);
+ if (vcp->vc_toserver)
+ iconv_close(vcp->vc_toserver);
+ smb_co_done(VCTOCP(vcp));
+ smb_sl_destroy(&vcp->vc_stlock);
+ free(vcp, M_SMBCONN);
+}
+
+/*
+ * Called when use count of VC dropped to zero.
+ * VC should be locked on enter with LK_DRAIN.
+ */
+static void
+smb_vc_gone(struct smb_connobj *cp, struct smb_cred *scred)
+{
+ struct smb_vc *vcp = CPTOVC(cp);
+
+ smb_vc_disconnect(vcp);
+}
+
+void
+smb_vc_ref(struct smb_vc *vcp, struct proc *p)
+{
+ smb_co_ref(VCTOCP(vcp), p);
+}
+
+void
+smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ smb_co_rele(VCTOCP(vcp), scred);
+}
+
+int
+smb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred)
+{
+ return smb_co_get(VCTOCP(vcp), flags, scred);
+}
+
+void
+smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ smb_co_put(VCTOCP(vcp), scred);
+}
+
+int
+smb_vc_lock(struct smb_vc *vcp, int flags, struct proc *p)
+{
+ return smb_co_lock(VCTOCP(vcp), flags, p);
+}
+
+void
+smb_vc_unlock(struct smb_vc *vcp, int flags, struct proc *p)
+{
+ smb_co_unlock(VCTOCP(vcp), flags, p);
+}
+
+int
+smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode)
+{
+ struct ucred *cred = scred->scr_cred;
+
+ if (smb_suser(cred) == 0 || cred->cr_uid == vcp->vc_uid)
+ return 0;
+ mode >>= 3;
+ if (!groupmember(vcp->vc_grp, cred))
+ mode >>= 3;
+ return (vcp->vc_mode & mode) == mode ? 0 : EACCES;
+}
+
+static int
+smb_vc_cmpshare(struct smb_share *ssp, struct smb_sharespec *dp)
+{
+ int exact = 1;
+
+ if (strcmp(ssp->ss_name, dp->name) != 0)
+ return 1;
+ if (dp->owner != SMBM_ANY_OWNER) {
+ if (ssp->ss_uid != dp->owner)
+ return 1;
+ } else
+ exact = 0;
+ if (dp->group != SMBM_ANY_GROUP) {
+ if (ssp->ss_grp != dp->group)
+ return 1;
+ } else
+ exact = 0;
+
+ if (dp->mode & SMBM_EXACT) {
+ if (!exact)
+ return 1;
+ return (dp->mode & SMBM_MASK) == ssp->ss_mode ? 0 : 1;
+ }
+ if (smb_share_access(ssp, dp->scred, dp->mode) != 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * Lookup share in the given VC. Share referenced and locked on return.
+ * VC expected to be locked on entry and will be left locked on exit.
+ */
+int
+smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *dp,
+ struct smb_cred *scred, struct smb_share **sspp)
+{
+ struct proc *p = scred->scr_p;
+ struct smb_share *ssp = NULL;
+ int error;
+
+ *sspp = NULL;
+ dp->scred = scred;
+ SMBCO_FOREACH((struct smb_connobj*)ssp, VCTOCP(vcp)) {
+ error = smb_share_lock(ssp, LK_EXCLUSIVE, p);
+ if (error)
+ continue;
+ if (smb_vc_cmpshare(ssp, dp) == 0)
+ break;
+ smb_share_unlock(ssp, 0, p);
+ }
+ if (ssp) {
+ smb_share_ref(ssp, p);
+ *sspp = ssp;
+ error = 0;
+ } else
+ error = ENOENT;
+ return error;
+}
+
+int
+smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred)
+{
+
+ return smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
+}
+
+/*
+ * Destroy VC to server, invalidate shares linked with it.
+ * Transport should be locked on entry.
+ */
+int
+smb_vc_disconnect(struct smb_vc *vcp)
+{
+
+ smb_iod_request(vcp->vc_iod, SMBIOD_EV_DISCONNECT | SMBIOD_EV_SYNC, NULL);
+ return 0;
+}
+
+static char smb_emptypass[] = "";
+
+const char *
+smb_vc_getpass(struct smb_vc *vcp)
+{
+ if (vcp->vc_pass)
+ return vcp->vc_pass;
+ return smb_emptypass;
+}
+
+static int
+smb_vc_getinfo(struct smb_vc *vcp, struct smb_vc_info *vip)
+{
+ bzero(vip, sizeof(struct smb_vc_info));
+ vip->itype = SMB_INFO_VC;
+ vip->usecount = vcp->obj.co_usecount;
+ vip->uid = vcp->vc_uid;
+ vip->gid = vcp->vc_grp;
+ vip->mode = vcp->vc_mode;
+ vip->flags = vcp->obj.co_flags;
+ vip->sopt = vcp->vc_sopt;
+ vip->iodstate = vcp->vc_iod->iod_state;
+ bzero(&vip->sopt.sv_skey, sizeof(vip->sopt.sv_skey));
+ snprintf(vip->srvname, sizeof(vip->srvname), "%s", vcp->vc_srvname);
+ snprintf(vip->vcname, sizeof(vip->vcname), "%s", vcp->vc_username);
+ return 0;
+}
+
+u_short
+smb_vc_nextmid(struct smb_vc *vcp)
+{
+ u_short r;
+
+ SMB_CO_LOCK(&vcp->obj);
+ r = vcp->vc_mid++;
+ SMB_CO_UNLOCK(&vcp->obj);
+ return r;
+}
+
+/*
+ * Share implementation
+ */
+/*
+ * Allocate share structure and attach it to the given VC
+ * Connection expected to be locked on entry. Share will be returned
+ * in locked state.
+ */
+int
+smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_share **sspp)
+{
+ struct smb_share *ssp;
+ struct proc *p = scred->scr_p;
+ struct ucred *cred = scred->scr_cred;
+ uid_t realuid = cred->cr_uid;
+ uid_t uid = shspec->owner;
+ gid_t gid = shspec->group;
+ int error, isroot;
+
+ isroot = smb_suser(cred) == 0;
+ /*
+ * Only superuser can create shares with different uid and gid
+ */
+ if (uid != SMBM_ANY_OWNER && uid != realuid && !isroot)
+ return EPERM;
+ if (gid != SMBM_ANY_GROUP && !groupmember(gid, cred) && !isroot)
+ return EPERM;
+ error = smb_vc_lookupshare(vcp, shspec, scred, &ssp);
+ if (!error) {
+ smb_share_put(ssp, scred);
+ return EEXIST;
+ }
+ if (uid == SMBM_ANY_OWNER)
+ uid = realuid;
+ if (gid == SMBM_ANY_GROUP)
+ gid = cred->cr_groups[0];
+ ssp = smb_zmalloc(sizeof(*ssp), M_SMBCONN, M_WAITOK);
+ smb_co_init(SSTOCP(ssp), SMBL_SHARE, "smbss", p);
+ ssp->obj.co_free = smb_share_free;
+ ssp->obj.co_gone = smb_share_gone;
+ smb_sl_init(&ssp->ss_stlock, "ssstlock");
+ ssp->ss_name = smb_strdup(shspec->name);
+ if (shspec->pass && shspec->pass[0])
+ ssp->ss_pass = smb_strdup(shspec->pass);
+ ssp->ss_type = shspec->stype;
+ ssp->ss_tid = SMB_TID_UNKNOWN;
+ ssp->ss_uid = uid;
+ ssp->ss_grp = gid;
+ ssp->ss_mode = shspec->rights & SMBM_MASK;
+ smb_co_addchild(VCTOCP(vcp), SSTOCP(ssp));
+ *sspp = ssp;
+ return 0;
+}
+
+static void
+smb_share_free(struct smb_connobj *cp)
+{
+ struct smb_share *ssp = CPTOSS(cp);
+
+ SMB_STRFREE(ssp->ss_name);
+ SMB_STRFREE(ssp->ss_pass);
+ smb_sl_destroy(&ssp->ss_stlock);
+ smb_co_done(SSTOCP(ssp));
+ free(ssp, M_SMBCONN);
+}
+
+static void
+smb_share_gone(struct smb_connobj *cp, struct smb_cred *scred)
+{
+ struct smb_share *ssp = CPTOSS(cp);
+
+ smb_smb_treedisconnect(ssp, scred);
+}
+
+void
+smb_share_ref(struct smb_share *ssp, struct proc *p)
+{
+ smb_co_ref(SSTOCP(ssp), p);
+}
+
+void
+smb_share_rele(struct smb_share *ssp, struct smb_cred *scred)
+{
+ smb_co_rele(SSTOCP(ssp), scred);
+}
+
+int
+smb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred)
+{
+ return smb_co_get(SSTOCP(ssp), flags, scred);
+}
+
+void
+smb_share_put(struct smb_share *ssp, struct smb_cred *scred)
+{
+ smb_co_put(SSTOCP(ssp), scred);
+}
+
+int
+smb_share_lock(struct smb_share *ssp, int flags, struct proc *p)
+{
+ return smb_co_lock(SSTOCP(ssp), flags, p);
+}
+
+void
+smb_share_unlock(struct smb_share *ssp, int flags, struct proc *p)
+{
+ smb_co_unlock(SSTOCP(ssp), flags, p);
+}
+
+int
+smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode)
+{
+ struct ucred *cred = scred->scr_cred;
+
+ if (smb_suser(cred) == 0 || cred->cr_uid == ssp->ss_uid)
+ return 0;
+ mode >>= 3;
+ if (!groupmember(ssp->ss_grp, cred))
+ mode >>= 3;
+ return (ssp->ss_mode & mode) == mode ? 0 : EACCES;
+}
+
+void
+smb_share_invalidate(struct smb_share *ssp)
+{
+ ssp->ss_tid = SMB_TID_UNKNOWN;
+}
+
+int
+smb_share_valid(struct smb_share *ssp)
+{
+ return ssp->ss_tid != SMB_TID_UNKNOWN &&
+ ssp->ss_vcgenid == SSTOVC(ssp)->vc_genid;
+}
+
+const char*
+smb_share_getpass(struct smb_share *ssp)
+{
+ struct smb_vc *vcp;
+
+ if (ssp->ss_pass)
+ return ssp->ss_pass;
+ vcp = SSTOVC(ssp);
+ if (vcp->vc_pass)
+ return vcp->vc_pass;
+ return smb_emptypass;
+}
+
+static int
+smb_share_getinfo(struct smb_share *ssp, struct smb_share_info *sip)
+{
+ bzero(sip, sizeof(struct smb_share_info));
+ sip->itype = SMB_INFO_SHARE;
+ sip->usecount = ssp->obj.co_usecount;
+ sip->tid = ssp->ss_tid;
+ sip->type= ssp->ss_type;
+ sip->uid = ssp->ss_uid;
+ sip->gid = ssp->ss_grp;
+ sip->mode= ssp->ss_mode;
+ sip->flags = ssp->obj.co_flags;
+ snprintf(sip->sname, sizeof(sip->sname), "%s", ssp->ss_name);
+ return 0;
+}
+
+/*
+ * Dump an entire tree into sysctl call
+ */
+static int
+smb_sysctl_treedump(SYSCTL_HANDLER_ARGS)
+{
+ struct proc *p = req->p;
+ struct smb_cred scred;
+ struct smb_vc *vcp;
+ struct smb_share *ssp;
+ struct smb_vc_info vci;
+ struct smb_share_info ssi;
+ int error, itype;
+
+ smb_makescred(&scred, p, p->p_ucred);
+ error = smb_sm_lockvclist(LK_SHARED, p);
+ if (error)
+ return error;
+ SMBCO_FOREACH((struct smb_connobj*)vcp, &smb_vclist) {
+ error = smb_vc_lock(vcp, LK_SHARED, p);
+ if (error)
+ continue;
+ smb_vc_getinfo(vcp, &vci);
+ error = SYSCTL_OUT(req, &vci, sizeof(struct smb_vc_info));
+ if (error) {
+ smb_vc_unlock(vcp, 0, p);
+ break;
+ }
+ SMBCO_FOREACH((struct smb_connobj*)ssp, VCTOCP(vcp)) {
+ error = smb_share_lock(ssp, LK_SHARED, p);
+ if (error) {
+ error = 0;
+ continue;
+ }
+ smb_share_getinfo(ssp, &ssi);
+ smb_share_unlock(ssp, 0, p);
+ error = SYSCTL_OUT(req, &ssi, sizeof(struct smb_share_info));
+ if (error)
+ break;
+ }
+ smb_vc_unlock(vcp, 0, p);
+ if (error)
+ break;
+ }
+ if (!error) {
+ itype = SMB_INFO_NONE;
+ error = SYSCTL_OUT(req, &itype, sizeof(itype));
+ }
+ smb_sm_unlockvclist(p);
+ return error;
+}
diff --git a/sys/netsmb/smb_conn.h b/sys/netsmb/smb_conn.h
new file mode 100644
index 0000000..3467f9a
--- /dev/null
+++ b/sys/netsmb/smb_conn.h
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NETINET_IN_H_
+#include <netinet/in.h>
+#endif
+
+/*
+ * Two levels of connection hierarchy
+ */
+#define SMBL_SM 0
+#define SMBL_VC 1
+#define SMBL_SHARE 2
+#define SMBL_NUM 3
+#define SMBL_NONE (-1)
+
+#define SMB_CS_NONE 0x0000
+#define SMB_CS_UPPER 0x0001 /* convert passed string to upper case */
+#define SMB_CS_LOWER 0x0002 /* convert passed string to lower case */
+
+/*
+ * Common object flags
+ */
+#define SMBO_GONE 0x1000000
+
+/*
+ * access modes
+ */
+#define SMBM_READ 0400 /* read conn attrs.(like list shares) */
+#define SMBM_WRITE 0200 /* modify conn attrs */
+#define SMBM_EXEC 0100 /* can send SMB requests */
+#define SMBM_READGRP 0040
+#define SMBM_WRITEGRP 0020
+#define SMBM_EXECGRP 0010
+#define SMBM_READOTH 0004
+#define SMBM_WRITEOTH 0002
+#define SMBM_EXECOTH 0001
+#define SMBM_MASK 0777
+#define SMBM_EXACT 010000 /* check for specified mode exactly */
+#define SMBM_ALL (SMBM_READ | SMBM_WRITE | SMBM_EXEC)
+#define SMBM_DEFAULT (SMBM_READ | SMBM_WRITE | SMBM_EXEC)
+#define SMBM_ANY_OWNER ((uid_t)-1)
+#define SMBM_ANY_GROUP ((gid_t)-1)
+
+/*
+ * VC flags
+ */
+#define SMBV_PERMANENT 0x0002
+#define SMBV_LONGNAMES 0x0004 /* connection is configured to use long names */
+#define SMBV_ENCRYPT 0x0008 /* server asked for encrypted password */
+#define SMBV_WIN95 0x0010 /* used to apply bugfixes for this OS */
+#define SMBV_PRIVATE 0x0020 /* connection can be used only by creator */
+#define SMBV_RECONNECTING 0x0040 /* conn is in the process of reconnection */
+#define SMBV_SINGLESHARE 0x0080 /* only one share connectin should be allowed */
+#define SMBV_CREATE 0x0100 /* lookup for create opeartion */
+/*#define SMBV_FAILED 0x0200*/ /* last reconnect attempt has failed */
+
+
+/*
+ * smb_share flags
+ */
+#define SMBS_PERMANENT 0x0001
+#define SMBS_RECONNECTING 0x0002
+#define SMBS_CONNECTED 0x0004
+
+/*
+ * share types
+ */
+#define SMB_ST_DISK 0x0 /* A: */
+#define SMB_ST_PRINTER 0x1 /* LPT: */
+#define SMB_ST_PIPE 0x2 /* IPC */
+#define SMB_ST_COMM 0x3 /* COMM */
+#define SMB_ST_ANY 0x4
+#define SMB_ST_MAX 0x4
+#define SMB_ST_NONE 0xff /* not a part of protocol */
+
+/*
+ * Negotiated protocol parameters
+ */
+struct smb_sopt {
+ int sv_proto;
+ int16_t sv_tz; /* offset in min relative to UTC */
+ u_int32_t sv_maxtx; /* maximum transmit buf size */
+ u_char sv_sm; /* security mode */
+ u_int16_t sv_maxmux; /* max number of outstanding rq's */
+ u_int16_t sv_maxvcs; /* max number of VCs */
+ u_int16_t sv_rawmode;
+ u_int32_t sv_maxraw; /* maximum raw-buffer size */
+ u_int32_t sv_skey; /* session key */
+ u_int32_t sv_caps; /* capabilites SMB_CAP_ */
+};
+
+/*
+ * network IO daemon states
+ */
+enum smbiod_state {
+ SMBIOD_ST_NOTCONN, /* no connect request was made */
+ SMBIOD_ST_RECONNECT, /* a [re]connect attempt is in progress */
+ SMBIOD_ST_TRANACTIVE, /* transport level is up */
+ SMBIOD_ST_VCACTIVE, /* session established */
+ SMBIOD_ST_DEAD /* connection broken, transport is down */
+};
+
+
+/*
+ * Info structures
+ */
+#define SMB_INFO_NONE 0
+#define SMB_INFO_VC 2
+#define SMB_INFO_SHARE 3
+
+struct smb_vc_info {
+ int itype;
+ int usecount;
+ uid_t uid; /* user id of connection */
+ gid_t gid; /* group of connection */
+ mode_t mode; /* access mode */
+ int flags;
+ enum smbiod_state iodstate;
+ struct smb_sopt sopt;
+ char srvname[SMB_MAXSRVNAMELEN];
+ char vcname[128];
+};
+
+struct smb_share_info {
+ int itype;
+ int usecount;
+ u_short tid; /* TID */
+ int type; /* share type */
+ uid_t uid; /* user id of connection */
+ gid_t gid; /* group of connection */
+ mode_t mode; /* access mode */
+ int flags;
+ char sname[128];
+};
+
+#ifdef _KERNEL
+
+#include <sys/lock.h>
+#include <netsmb/smb_subr.h>
+
+#define CONNADDREQ(a1,a2) ((a1)->sa_len == (a2)->sa_len && \
+ bcmp(a1, a2, (a1)->sa_len) == 0)
+
+struct smb_vc;
+struct smb_share;
+struct smb_cred;
+struct smb_rq;
+struct mbdata;
+struct smbioc_oshare;
+struct smbioc_ossn;
+struct uio;
+
+TAILQ_HEAD(smb_rqhead, smb_rq);
+
+#define SMB_DEFRQTIMO 5
+
+#define SMB_DIALECT(vcp) ((vcp)->vc_sopt.sv_proto)
+
+struct smb_tran_desc;
+
+/*
+ * Connection object
+ */
+struct smb_connobj;
+
+typedef void smb_co_gone_t (struct smb_connobj *cp, struct smb_cred *scred);
+typedef void smb_co_free_t (struct smb_connobj *cp);
+
+#define SMB_CO_LOCK(cp) smb_sl_lock(&(cp)->co_interlock)
+#define SMB_CO_UNLOCK(cp) smb_sl_unlock(&(cp)->co_interlock)
+
+struct smb_connobj {
+ int co_level; /* SMBL_ */
+ int co_flags;
+ struct lock co_lock;
+ struct smb_slock co_interlock;
+ int co_usecount;
+ struct smb_connobj * co_parent;
+ SLIST_HEAD(,smb_connobj)co_children;
+ SLIST_ENTRY(smb_connobj)co_next;
+ smb_co_gone_t * co_gone;
+ smb_co_free_t * co_free;
+};
+
+#define SMBCO_FOREACH(var, cp) SLIST_FOREACH((var), &(cp)->co_children, co_next)
+
+/*
+ * Virtual Circuit (session) to a server.
+ * This is the most (over)complicated part of SMB protocol.
+ * For the user security level (usl), each session with different remote
+ * user name has its own VC.
+ * It is unclear however, should share security level (ssl) allow additional
+ * VCs, because user name is not used and can be the same. On other hand,
+ * multiple VCs allows us to create separate sessions to server on a per
+ * user basis.
+ */
+
+/*
+ * This lock protects vc_flags
+ */
+#define SMBC_ST_LOCK(vcp) smb_sl_lock(&(vcp)->vc_stlock)
+#define SMBC_ST_UNLOCK(vcp) smb_sl_unlock(&(vcp)->vc_stlock)
+
+
+struct smb_vc {
+ struct smb_connobj obj;
+ char * vc_srvname;
+ struct sockaddr*vc_paddr; /* server addr */
+ struct sockaddr*vc_laddr; /* local addr, if any */
+ char * vc_username;
+ char * vc_pass; /* password for usl case */
+ char * vc_domain; /* workgroup/primary domain */
+
+ u_int vc_timo; /* default request timeout */
+ int vc_maxvcs; /* maximum number of VC per connection */
+
+ void * vc_tolower; /* local charset */
+ void * vc_toupper; /* local charset */
+ void * vc_toserver; /* local charset to server one */
+ void * vc_tolocal; /* server charset to local one */
+ int vc_number; /* number of this VC from the client side */
+ int vc_genid;
+ uid_t vc_uid; /* user id of connection */
+ gid_t vc_grp; /* group of connection */
+ mode_t vc_mode; /* access mode */
+ struct tnode * vc_tnode; /* backing object */
+ u_short vc_smbuid; /* unique vc id assigned by server */
+
+ u_char vc_hflags; /* or'ed with flags in the smb header */
+ u_short vc_hflags2; /* or'ed with flags in the smb header */
+ void * vc_tdata; /* transport control block */
+ struct smb_tran_desc *vc_tdesc;
+ int vc_chlen; /* actual challenge length */
+ u_char vc_ch[SMB_MAXCHALLENGELEN];
+ u_short vc_mid; /* multiplex id */
+ struct smb_sopt vc_sopt; /* server options */
+ struct smb_cred*vc_scred; /* used in reconnect procedure */
+ int vc_txmax; /* max tx/rx packet size */
+ struct smbiod * vc_iod;
+ struct smb_slock vc_stlock;
+};
+
+#define vc_maxmux vc_sopt.sv_maxmux
+#define vc_flags obj.co_flags
+
+
+/*
+ * smb_share structure describes connection to the given SMB share (tree).
+ * Connection to share is always built on top of the VC.
+ */
+
+/*
+ * This lock protects ss_flags
+ */
+#define SMBS_ST_LOCK(ssp) smb_sl_lock(&(ssp)->ss_stlock)
+#define SMBS_ST_LOCKPTR(ssp) (&(ssp)->ss_stlock)
+#define SMBS_ST_UNLOCK(ssp) smb_sl_unlock(&(ssp)->ss_stlock)
+
+struct smb_share {
+ struct smb_connobj obj;
+ char * ss_name;
+ u_short ss_tid; /* TID */
+ int ss_type; /* share type */
+ uid_t ss_uid; /* user id of connection */
+ gid_t ss_grp; /* group of connection */
+ mode_t ss_mode; /* access mode */
+ int ss_vcgenid;
+ char * ss_pass; /* password to a share, can be null */
+ struct smb_slock ss_stlock;
+ struct smb_cred *ss_cred; /* used in reconnect procedure */
+};
+
+#define ss_flags obj.co_flags
+
+#define CPTOVC(cp) ((struct smb_vc*)(cp))
+#define VCTOCP(vcp) (&(vcp)->obj)
+#define CPTOSS(cp) ((struct smb_share*)(cp))
+#define SSTOVC(ssp) CPTOVC(((ssp)->obj.co_parent))
+#define SSTOCP(ssp) (&(ssp)->obj)
+
+struct smb_vcspec {
+ char * srvname;
+ struct sockaddr*sap;
+ struct sockaddr*lap;
+ int flags;
+ char * username;
+ char * pass;
+ char * domain;
+ mode_t mode;
+ mode_t rights;
+ uid_t owner;
+ gid_t group;
+ char * localcs;
+ char * servercs;
+ struct smb_sharespec *shspec;
+ struct smb_share *ssp; /* returned */
+ /*
+ * The rest is an internal data
+ */
+ struct smb_cred *scred;
+};
+
+struct smb_sharespec {
+ char * name;
+ char * pass;
+ mode_t mode;
+ mode_t rights;
+ uid_t owner;
+ gid_t group;
+ int stype;
+ /*
+ * The rest is an internal data
+ */
+ struct smb_cred *scred;
+};
+
+/*
+ * Session level functions
+ */
+int smb_sm_init(void);
+int smb_sm_done(void);
+int smb_sm_lookup(struct smb_vcspec *vcspec,
+ struct smb_sharespec *shspec, struct smb_cred *scred,
+ struct smb_vc **vcpp);
+
+/*
+ * Connection object
+ */
+void smb_co_ref(struct smb_connobj *cp, struct proc *p);
+void smb_co_rele(struct smb_connobj *cp, struct smb_cred *scred);
+int smb_co_get(struct smb_connobj *cp, int flags, struct smb_cred *scred);
+void smb_co_put(struct smb_connobj *cp, struct smb_cred *scred);
+int smb_co_lock(struct smb_connobj *cp, int flags, struct proc *p);
+void smb_co_unlock(struct smb_connobj *cp, int flags, struct proc *p);
+
+/*
+ * session level functions
+ */
+int smb_vc_create(struct smb_vcspec *vcspec,
+ struct smb_cred *scred, struct smb_vc **vcpp);
+int smb_vc_connect(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_vc_access(struct smb_vc *vcp, struct smb_cred *scred, mode_t mode);
+int smb_vc_get(struct smb_vc *vcp, int flags, struct smb_cred *scred);
+void smb_vc_put(struct smb_vc *vcp, struct smb_cred *scred);
+void smb_vc_ref(struct smb_vc *vcp, struct proc *p);
+void smb_vc_rele(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_vc_lock(struct smb_vc *vcp, int flags, struct proc *p);
+void smb_vc_unlock(struct smb_vc *vcp, int flags, struct proc *p);
+int smb_vc_lookupshare(struct smb_vc *vcp, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_share **sspp);
+const char * smb_vc_getpass(struct smb_vc *vcp);
+u_short smb_vc_nextmid(struct smb_vc *vcp);
+
+/*
+ * share level functions
+ */
+int smb_share_create(struct smb_vc *vcp, struct smb_sharespec *shspec,
+ struct smb_cred *scred, struct smb_share **sspp);
+int smb_share_access(struct smb_share *ssp, struct smb_cred *scred, mode_t mode);
+void smb_share_ref(struct smb_share *ssp, struct proc *p);
+void smb_share_rele(struct smb_share *ssp, struct smb_cred *scred);
+int smb_share_get(struct smb_share *ssp, int flags, struct smb_cred *scred);
+void smb_share_put(struct smb_share *ssp, struct smb_cred *scred);
+int smb_share_lock(struct smb_share *ssp, int flags, struct proc *p);
+void smb_share_unlock(struct smb_share *ssp, int flags, struct proc *p);
+void smb_share_invalidate(struct smb_share *ssp);
+int smb_share_valid(struct smb_share *ssp);
+const char * smb_share_getpass(struct smb_share *ssp);
+
+/*
+ * SMB protocol level functions
+ */
+int smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred);
+int smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred);
+int smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred);
+int smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
+ struct smb_cred *scred);
+int smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
+ struct smb_cred *scred);
+int smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred);
+
+/*
+ * smbiod thread
+ */
+
+#define SMBIOD_EV_NEWRQ 0x0001
+#define SMBIOD_EV_SHUTDOWN 0x0002
+#define SMBIOD_EV_CONNECT 0x0003
+#define SMBIOD_EV_DISCONNECT 0x0004
+#define SMBIOD_EV_TREECONNECT 0x0005
+#define SMBIOD_EV_MASK 0x00ff
+#define SMBIOD_EV_SYNC 0x0100
+#define SMBIOD_EV_PROCESSING 0x0200
+
+struct smbiod_event {
+ int ev_type;
+ int ev_error;
+ void * ev_ident;
+ STAILQ_ENTRY(smbiod_event) ev_link;
+};
+
+#define SMBIOD_SHUTDOWN 0x0001
+
+struct smbiod {
+ int iod_id;
+ int iod_flags;
+ enum smbiod_state iod_state;
+ int iod_muxcnt; /* number of active outstanding requests */
+ int iod_sleeptimo;
+ struct smb_vc * iod_vc;
+ struct smb_slock iod_rqlock; /* iod_rqlist, iod_muxwant */
+ struct smb_rqhead iod_rqlist; /* list of outstanding requests */
+ int iod_muxwant;
+ struct proc * iod_p;
+ struct smb_cred iod_scred;
+ struct smb_slock iod_evlock; /* iod_evlist */
+ STAILQ_HEAD(,smbiod_event) iod_evlist;
+ struct timespec iod_lastrqsent;
+ struct timespec iod_pingtimo;
+};
+
+int smb_iod_init(void);
+int smb_iod_done(void);
+int smb_iod_create(struct smb_vc *vcp);
+int smb_iod_destroy(struct smbiod *iod);
+int smb_iod_request(struct smbiod *iod, int event, void *ident);
+int smb_iod_addrq(struct smb_rq *rqp);
+int smb_iod_waitrq(struct smb_rq *rqp);
+int smb_iod_removerq(struct smb_rq *rqp);
+
+#endif /* _KERNEL */
diff --git a/sys/netsmb/smb_crypt.c b/sys/netsmb/smb_crypt.c
new file mode 100644
index 0000000..e72a385
--- /dev/null
+++ b/sys/netsmb/smb_crypt.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+
+#include <sys/md4.h>
+#include <sys/iconv.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_dev.h>
+
+#include "opt_netsmb.h"
+
+#ifdef NETSMBCRYPTO
+
+#include <crypto/des/des.h>
+
+static u_char N8[] = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25};
+
+
+static void
+smb_E(const u_char *key, u_char *data, u_char *dest)
+{
+ des_key_schedule *ksp;
+ u_char kk[8];
+
+ kk[0] = key[0] & 0xfe;
+ kk[1] = key[0] << 7 | (key[1] >> 1 & 0xfe);
+ kk[2] = key[1] << 6 | (key[2] >> 2 & 0xfe);
+ kk[3] = key[2] << 5 | (key[3] >> 3 & 0xfe);
+ kk[4] = key[3] << 4 | (key[4] >> 4 & 0xfe);
+ kk[5] = key[4] << 3 | (key[5] >> 5 & 0xfe);
+ kk[6] = key[5] << 2 | (key[6] >> 6 & 0xfe);
+ kk[7] = key[6] << 1;
+ ksp = malloc(sizeof(des_key_schedule), M_SMBTEMP, M_WAITOK);
+ des_set_key((C_Block*)kk, *ksp);
+ des_ecb_encrypt((C_Block*)data, (C_Block*)dest, *ksp, 1);
+ free(ksp, M_SMBTEMP);
+}
+#endif
+
+
+int
+smb_encrypt(const u_char *apwd, u_char *C8, u_char *RN)
+{
+#ifdef NETSMBCRYPTO
+ u_char *p, *P14, *S21;
+
+ p = malloc(14 + 21, M_SMBTEMP, M_WAITOK);
+ bzero(p, 14 + 21);
+ P14 = p;
+ S21 = p + 14;
+ bcopy(apwd, P14, min(14, strlen(apwd)));
+ /*
+ * S21 = concat(Ex(P14, N8), zeros(5));
+ */
+ smb_E(P14, N8, S21);
+ smb_E(P14 + 7, N8, S21 + 8);
+
+ smb_E(S21, C8, RN);
+ smb_E(S21 + 7, C8, RN + 8);
+ smb_E(S21 + 14, C8, RN + 16);
+ free(p, M_SMBTEMP);
+ return 0;
+#else
+ SMBERROR("password encryption is not available\n");
+ bzero(RN, 24);
+ return EAUTH;
+#endif
+}
+
+int
+smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN)
+{
+#ifdef NETSMBCRYPTO
+ u_char S21[21];
+ u_int16_t *unipwd;
+ MD4_CTX *ctxp;
+ int len;
+
+ len = strlen(apwd);
+ unipwd = malloc(len * sizeof(u_int16_t), M_SMBTEMP, M_WAITOK);
+ /*
+ * S21 = concat(MD4(U(apwd)), zeros(5));
+ */
+ smb_strtouni(unipwd, apwd);
+ ctxp = malloc(sizeof(MD4_CTX), M_SMBTEMP, M_WAITOK);
+ MD4Init(ctxp);
+ MD4Update(ctxp, (u_char*)unipwd, len * sizeof(u_int16_t));
+ free(unipwd, M_SMBTEMP);
+ bzero(S21, 21);
+ MD4Final(S21, ctxp);
+ free(ctxp, M_SMBTEMP);
+
+ smb_E(S21, C8, RN);
+ smb_E(S21 + 7, C8, RN + 8);
+ smb_E(S21 + 14, C8, RN + 16);
+ return 0;
+#else
+ SMBERROR("password encryption is not available\n");
+ bzero(RN, 24);
+ return EAUTH;
+#endif
+}
+
diff --git a/sys/netsmb/smb_dev.c b/sys/netsmb/smb_dev.c
new file mode 100644
index 0000000..fe00717
--- /dev/null
+++ b/sys/netsmb/smb_dev.c
@@ -0,0 +1,448 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/ioccom.h>
+#include <sys/malloc.h>
+#include <sys/uio.h>
+#include <sys/conf.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/poll.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+#include <sys/vnode.h>
+
+#include <net/if.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_dev.h>
+
+#define SMB_GETDEV(dev) ((struct smb_dev*)(dev)->si_drv1)
+#define SMB_CHECKMINOR(dev) do { \
+ sdp = SMB_GETDEV(dev); \
+ if (sdp == NULL) return ENXIO; \
+ } while(0)
+
+static d_open_t nsmb_dev_open;
+static d_close_t nsmb_dev_close;
+static d_read_t nsmb_dev_read;
+static d_write_t nsmb_dev_write;
+static d_ioctl_t nsmb_dev_ioctl;
+static d_poll_t nsmb_dev_poll;
+
+MODULE_DEPEND(netsmb, libiconv, 1, 1, 1);
+MODULE_VERSION(netsmb, NSMB_VERSION);
+
+static int smb_version = NSMB_VERSION;
+
+
+SYSCTL_DECL(_net_smb);
+SYSCTL_INT(_net_smb, OID_AUTO, version, CTLFLAG_RD, &smb_version, 0, "");
+
+static MALLOC_DEFINE(M_NSMBDEV, "NETSMBDEV", "NET/SMB device");
+
+
+/*
+int smb_dev_queue(struct smb_dev *ndp, struct smb_rq *rqp, int prio);
+*/
+
+static struct cdevsw nsmb_cdevsw = {
+ /* open */ nsmb_dev_open,
+ /* close */ nsmb_dev_close,
+ /* read */ nsmb_dev_read,
+ /* write */ nsmb_dev_write,
+ /* ioctl */ nsmb_dev_ioctl,
+ /* poll */ nsmb_dev_poll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ NSMB_NAME,
+ /* maj */ NSMB_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+#ifndef FB_CURRENT
+ /* bmaj */ -1
+#endif
+};
+
+static eventhandler_tag nsmb_dev_tag;
+
+static void
+nsmb_dev_clone(void *arg, char *name, int namelen, dev_t *dev)
+{
+ int min;
+
+ if (*dev != NODEV)
+ return;
+ if (dev_stdclone(name, NULL, NSMB_NAME, &min) != 1)
+ return;
+ *dev = make_dev(&nsmb_cdevsw, min, 0, 0, 0600, NSMB_NAME"%d", min);
+}
+
+static int
+nsmb_dev_open(dev_t dev, int oflags, int devtype, struct proc *p)
+{
+ struct smb_dev *sdp;
+ struct ucred *cred = p->p_ucred;
+ int s;
+
+ sdp = SMB_GETDEV(dev);
+ if (sdp && (sdp->sd_flags & NSMBFL_OPEN))
+ return EBUSY;
+ if (sdp == NULL) {
+ sdp = malloc(sizeof(*sdp), M_NSMBDEV, M_WAITOK);
+ dev->si_drv1 = (void*)sdp;
+ }
+ /*
+ * XXX: this is just crazy - make a device for an already passed device...
+ * someone should take care of it.
+ */
+ if ((dev->si_flags & SI_NAMED) == 0)
+ make_dev(&nsmb_cdevsw, minor(dev), cred->cr_uid, cred->cr_gid, 0700,
+ NSMB_NAME"%d", dev2unit(dev));
+ bzero(sdp, sizeof(*sdp));
+/*
+ STAILQ_INIT(&sdp->sd_rqlist);
+ STAILQ_INIT(&sdp->sd_rplist);
+ bzero(&sdp->sd_pollinfo, sizeof(struct selinfo));
+*/
+ s = splimp();
+ sdp->sd_level = -1;
+ sdp->sd_flags |= NSMBFL_OPEN;
+ splx(s);
+ return 0;
+}
+
+static int
+nsmb_dev_close(dev_t dev, int flag, int fmt, struct proc *p)
+{
+ struct smb_dev *sdp;
+ struct smb_vc *vcp;
+ struct smb_share *ssp;
+ struct smb_cred scred;
+ int s;
+
+ SMB_CHECKMINOR(dev);
+ s = splimp();
+ if ((sdp->sd_flags & NSMBFL_OPEN) == 0) {
+ splx(s);
+ return EBADF;
+ }
+ smb_makescred(&scred, p, NULL);
+ ssp = sdp->sd_share;
+ if (ssp != NULL)
+ smb_share_rele(ssp, &scred);
+ vcp = sdp->sd_vc;
+ if (vcp != NULL)
+ smb_vc_rele(vcp, &scred);
+/*
+ smb_flushq(&sdp->sd_rqlist);
+ smb_flushq(&sdp->sd_rplist);
+*/
+#if __FreeBSD_version > 400001
+ dev->si_drv1 = NULL;
+ free(sdp, M_NSMBDEV);
+ destroy_dev(dev);
+#else
+ sdp->sd_flags &= ~NSMBFL_OPEN;
+#endif
+ splx(s);
+ return 0;
+}
+
+
+static int
+nsmb_dev_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
+{
+ struct smb_dev *sdp;
+ struct smb_vc *vcp;
+ struct smb_share *ssp;
+ struct smb_cred scred;
+ int error = 0;
+
+ SMB_CHECKMINOR(dev);
+ if ((sdp->sd_flags & NSMBFL_OPEN) == 0)
+ return EBADF;
+
+ smb_makescred(&scred, p, NULL);
+ switch (cmd) {
+ case SMBIOC_OPENSESSION:
+ if (sdp->sd_vc)
+ return EISCONN;
+ error = smb_usr_opensession((struct smbioc_ossn*)data,
+ &scred, &vcp);
+ if (error)
+ break;
+ sdp->sd_vc = vcp;
+ smb_vc_unlock(vcp, 0, p);
+ sdp->sd_level = SMBL_VC;
+ break;
+ case SMBIOC_OPENSHARE:
+ if (sdp->sd_share)
+ return EISCONN;
+ if (sdp->sd_vc == NULL)
+ return ENOTCONN;
+ error = smb_usr_openshare(sdp->sd_vc,
+ (struct smbioc_oshare*)data, &scred, &ssp);
+ if (error)
+ break;
+ sdp->sd_share = ssp;
+ smb_share_unlock(ssp, 0, p);
+ sdp->sd_level = SMBL_SHARE;
+ break;
+ case SMBIOC_REQUEST:
+ if (sdp->sd_share == NULL)
+ return ENOTCONN;
+ error = smb_usr_simplerequest(sdp->sd_share,
+ (struct smbioc_rq*)data, &scred);
+ break;
+ case SMBIOC_T2RQ:
+ if (sdp->sd_share == NULL)
+ return ENOTCONN;
+ error = smb_usr_t2request(sdp->sd_share,
+ (struct smbioc_t2rq*)data, &scred);
+ break;
+ case SMBIOC_SETFLAGS: {
+ struct smbioc_flags *fl = (struct smbioc_flags*)data;
+ int on;
+
+ if (fl->ioc_level == SMBL_VC) {
+ if (fl->ioc_mask & SMBV_PERMANENT) {
+ on = fl->ioc_flags & SMBV_PERMANENT;
+ if ((vcp = sdp->sd_vc) == NULL)
+ return ENOTCONN;
+ error = smb_vc_get(vcp, LK_EXCLUSIVE, &scred);
+ if (error)
+ break;
+ if (on && (vcp->obj.co_flags & SMBV_PERMANENT) == 0) {
+ vcp->obj.co_flags |= SMBV_PERMANENT;
+ smb_vc_ref(vcp, p);
+ } else if (!on && (vcp->obj.co_flags & SMBV_PERMANENT)) {
+ vcp->obj.co_flags &= ~SMBV_PERMANENT;
+ smb_vc_rele(vcp, &scred);
+ }
+ smb_vc_put(vcp, &scred);
+ } else
+ error = EINVAL;
+ } else if (fl->ioc_level == SMBL_SHARE) {
+ if (fl->ioc_mask & SMBS_PERMANENT) {
+ on = fl->ioc_flags & SMBS_PERMANENT;
+ if ((ssp = sdp->sd_share) == NULL)
+ return ENOTCONN;
+ error = smb_share_get(ssp, LK_EXCLUSIVE, &scred);
+ if (error)
+ break;
+ if (on && (ssp->obj.co_flags & SMBS_PERMANENT) == 0) {
+ ssp->obj.co_flags |= SMBS_PERMANENT;
+ smb_share_ref(ssp, p);
+ } else if (!on && (ssp->obj.co_flags & SMBS_PERMANENT)) {
+ ssp->obj.co_flags &= ~SMBS_PERMANENT;
+ smb_share_rele(ssp, &scred);
+ }
+ smb_share_put(ssp, &scred);
+ } else
+ error = EINVAL;
+ break;
+ } else
+ error = EINVAL;
+ break;
+ }
+ case SMBIOC_LOOKUP:
+ if (sdp->sd_vc || sdp->sd_share)
+ return EISCONN;
+ vcp = NULL;
+ ssp = NULL;
+ error = smb_usr_lookup((struct smbioc_lookup*)data, &scred, &vcp, &ssp);
+ if (error)
+ break;
+ if (vcp) {
+ sdp->sd_vc = vcp;
+ smb_vc_unlock(vcp, 0, p);
+ sdp->sd_level = SMBL_VC;
+ }
+ if (ssp) {
+ sdp->sd_share = ssp;
+ smb_share_unlock(ssp, 0, p);
+ sdp->sd_level = SMBL_SHARE;
+ }
+ break;
+ case SMBIOC_READ: case SMBIOC_WRITE: {
+ struct smbioc_rw *rwrq = (struct smbioc_rw*)data;
+ struct uio auio;
+ struct iovec iov;
+
+ if ((ssp = sdp->sd_share) == NULL)
+ return ENOTCONN;
+ iov.iov_base = rwrq->ioc_base;
+ iov.iov_len = rwrq->ioc_cnt;
+ auio.uio_iov = &iov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = rwrq->ioc_offset;
+ auio.uio_resid = rwrq->ioc_cnt;
+ auio.uio_segflg = UIO_USERSPACE;
+ auio.uio_rw = (cmd == SMBIOC_READ) ? UIO_READ : UIO_WRITE;
+ auio.uio_procp = p;
+ if (cmd == SMBIOC_READ)
+ error = smb_read(ssp, rwrq->ioc_fh, &auio, &scred);
+ else
+ error = smb_write(ssp, rwrq->ioc_fh, &auio, &scred);
+ rwrq->ioc_cnt -= auio.uio_resid;
+ break;
+ }
+ default:
+ error = ENODEV;
+ }
+ return error;
+}
+
+static int
+nsmb_dev_read(dev_t dev, struct uio *uio, int flag)
+{
+ return EACCES;
+}
+
+static int
+nsmb_dev_write(dev_t dev, struct uio *uio, int flag)
+{
+ return EACCES;
+}
+
+static int
+nsmb_dev_poll(dev_t dev, int events, struct proc *p)
+{
+ return ENODEV;
+}
+
+static int
+nsmb_dev_load(module_t mod, int cmd, void *arg)
+{
+ int error = 0;
+
+ switch (cmd) {
+ case MOD_LOAD:
+ error = smb_sm_init();
+ if (error)
+ break;
+ error = smb_iod_init();
+ if (error) {
+ smb_sm_done();
+ break;
+ }
+#if __FreeBSD_version > 400001
+ cdevsw_add(&nsmb_cdevsw);
+#endif
+#if __FreeBSD_version > 500000
+ nsmb_dev_tag = EVENTHANDLER_REGISTER(dev_clone, nsmb_dev_clone, 0, 1000);
+#endif
+ printf("netsmb_dev: loaded\n");
+ break;
+ case MOD_UNLOAD:
+ smb_iod_done();
+ error = smb_sm_done();
+ error = 0;
+#if __FreeBSD_version > 500000
+ EVENTHANDLER_DEREGISTER(dev_clone, nsmb_dev_tag);
+#endif
+#if __FreeBSD_version > 400001
+ cdevsw_remove(&nsmb_cdevsw);
+#endif
+ printf("netsmb_dev: unloaded\n");
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ return error;
+}
+
+#if __FreeBSD_version > 400000
+DEV_MODULE (dev_netsmb, nsmb_dev_load, 0);
+#else
+CDEV_MODULE(dev_netsmb, NSMB_MAJOR, nsmb_cdevsw, nsmb_dev_load, 0);
+#endif
+
+
+/*
+ * Convert a file descriptor to appropriate smb_share pointer
+ */
+static struct file*
+nsmb_getfp(struct filedesc* fdp, int fd, int flag)
+{
+ struct file* fp;
+
+ if (((u_int)fd) >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[fd]) == NULL ||
+ (fp->f_flag & flag) == 0)
+ return (NULL);
+ return (fp);
+}
+
+int
+smb_dev2share(int fd, int mode, struct smb_cred *scred,
+ struct smb_share **sspp)
+{
+ struct file *fp;
+ struct vnode *vp;
+ struct smb_dev *sdp;
+ struct smb_share *ssp;
+ dev_t dev;
+ int error;
+
+ if ((fp = nsmb_getfp(scred->scr_p->p_fd, fd, FREAD | FWRITE)) == NULL)
+ return EBADF;
+ vp = (struct vnode*)fp->f_data;
+ if (vp == NULL)
+ return EBADF;
+ dev = vn_todev(vp);
+ if (dev == NODEV)
+ return EBADF;
+ SMB_CHECKMINOR(dev);
+ ssp = sdp->sd_share;
+ if (ssp == NULL)
+ return ENOTCONN;
+ error = smb_share_get(ssp, LK_EXCLUSIVE, scred);
+ if (error)
+ return error;
+ *sspp = ssp;
+ return 0;
+}
+
diff --git a/sys/netsmb/smb_dev.h b/sys/netsmb/smb_dev.h
new file mode 100644
index 0000000..52ab436
--- /dev/null
+++ b/sys/netsmb/smb_dev.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NETSMB_DEV_H_
+#define _NETSMB_DEV_H_
+
+#define NSMB_NAME "nsmb"
+#define NSMB_MAJOR 144
+
+#define NSMB_VERMAJ 1
+#define NSMB_VERMIN 3006
+#define NSMB_VERSION (NSMB_VERMAJ * 100000 + NSMB_VERMIN)
+
+#define NSMBFL_OPEN 0x0001
+
+#define SMBVOPT_CREATE 0x0001 /* create object if necessary */
+#define SMBVOPT_PRIVATE 0x0002 /* connection should be private */
+#define SMBVOPT_SINGLESHARE 0x0004 /* keep only ane share at this VC */
+#define SMBVOPT_PERMANENT 0x0010 /* object will keep last reference */
+
+#define SMBSOPT_CREATE 0x0001 /* create object if necessary */
+#define SMBSOPT_PERMANENT 0x0010 /* object will keep last reference */
+
+/*
+ * SMBIOC_LOOKUP flags
+ */
+#define SMBLK_CREATE 0x0001
+
+struct smbioc_ossn {
+ int ioc_opt;
+ int ioc_svlen; /* size of ioc_server address */
+ struct sockaddr*ioc_server;
+ int ioc_lolen; /* size of ioc_local address */
+ struct sockaddr*ioc_local;
+ char ioc_srvname[SMB_MAXSRVNAMELEN + 1];
+ int ioc_timeout;
+ int ioc_retrycount; /* number of retries before giveup */
+ char ioc_localcs[16];/* local charset */
+ char ioc_servercs[16];/* server charset */
+ char ioc_user[SMB_MAXUSERNAMELEN + 1];
+ char ioc_workgroup[SMB_MAXUSERNAMELEN + 1];
+ char ioc_password[SMB_MAXPASSWORDLEN + 1];
+ uid_t ioc_owner; /* proposed owner */
+ gid_t ioc_group; /* proposed group */
+ mode_t ioc_mode; /* desired access mode */
+ mode_t ioc_rights; /* SMBM_* */
+};
+
+struct smbioc_oshare {
+ int ioc_opt;
+ int ioc_stype; /* share type */
+ char ioc_share[SMB_MAXSHARENAMELEN + 1];
+ char ioc_password[SMB_MAXPASSWORDLEN + 1];
+ uid_t ioc_owner; /* proposed owner of share */
+ gid_t ioc_group; /* proposed group of share */
+ mode_t ioc_mode; /* desired access mode to share */
+ mode_t ioc_rights; /* SMBM_* */
+};
+
+struct smbioc_rq {
+ u_char ioc_cmd;
+ u_char ioc_twc;
+ void * ioc_twords;
+ u_short ioc_tbc;
+ void * ioc_tbytes;
+ int ioc_rpbufsz;
+ char * ioc_rpbuf;
+ u_char ioc_rwc;
+ u_short ioc_rbc;
+ u_int8_t ioc_errclass;
+ u_int16_t ioc_serror;
+ u_int32_t ioc_error;
+};
+
+struct smbioc_t2rq {
+ u_int16_t ioc_setup[3];
+ int ioc_setupcnt;
+ char * ioc_name;
+ u_short ioc_tparamcnt;
+ void * ioc_tparam;
+ u_short ioc_tdatacnt;
+ void * ioc_tdata;
+ u_short ioc_rparamcnt;
+ void * ioc_rparam;
+ u_short ioc_rdatacnt;
+ void * ioc_rdata;
+};
+
+struct smbioc_flags {
+ int ioc_level; /* 0 - session, 1 - share */
+ int ioc_mask;
+ int ioc_flags;
+};
+
+struct smbioc_lookup {
+ int ioc_level;
+ int ioc_flags;
+ struct smbioc_ossn ioc_ssn;
+ struct smbioc_oshare ioc_sh;
+};
+
+struct smbioc_rw {
+ smbfh ioc_fh;
+ char * ioc_base;
+ off_t ioc_offset;
+ int ioc_cnt;
+};
+
+/*
+ * Device IOCTLs
+ */
+#define SMBIOC_OPENSESSION _IOW('n', 100, struct smbioc_ossn)
+#define SMBIOC_OPENSHARE _IOW('n', 101, struct smbioc_oshare)
+#define SMBIOC_REQUEST _IOWR('n', 102, struct smbioc_rq)
+#define SMBIOC_T2RQ _IOWR('n', 103, struct smbioc_t2rq)
+#define SMBIOC_SETFLAGS _IOW('n', 104, struct smbioc_flags)
+#define SMBIOC_LOOKUP _IOW('n', 106, struct smbioc_lookup)
+#define SMBIOC_READ _IOWR('n', 107, struct smbioc_rw)
+#define SMBIOC_WRITE _IOWR('n', 108, struct smbioc_rw)
+
+#ifdef _KERNEL
+
+#ifndef LK_SHARED
+#include <sys/lock.h>
+#endif
+
+#define IF_QEMPTY(ifq) ((ifq)->ifq_len == 0)
+
+#define SMBST_CONNECTED 1
+
+STAILQ_HEAD(smbrqh, smb_rq);
+
+struct smb_dev {
+ int sd_opened;
+ int sd_level;
+ struct smb_vc * sd_vc; /* reference to VC */
+ struct smb_share *sd_share; /* reference to share if any */
+ int sd_poll;
+ int sd_seq;
+/* struct ifqueue sd_rdqueue;
+ struct ifqueue sd_wrqueue;
+ struct selinfo sd_pollinfo;
+ struct smbrqh sd_rqlist;
+ struct smbrqh sd_rplist;
+ struct ucred *sd_owner;*/
+ int sd_flags;
+};
+
+struct smb_cred;
+/*
+ * Compound user interface
+ */
+int smb_usr_lookup(struct smbioc_lookup *dp, struct smb_cred *scred,
+ struct smb_vc **vcpp, struct smb_share **sspp);
+int smb_usr_opensession(struct smbioc_ossn *data,
+ struct smb_cred *scred, struct smb_vc **vcpp);
+int smb_usr_openshare(struct smb_vc *vcp, struct smbioc_oshare *data,
+ struct smb_cred *scred, struct smb_share **sspp);
+int smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *data,
+ struct smb_cred *scred);
+int smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *data,
+ struct smb_cred *scred);
+int smb_dev2share(int fd, int mode, struct smb_cred *scred,
+ struct smb_share **sspp);
+
+
+#endif /* _KERNEL */
+
+#endif /* _NETSMB_DEV_H_ */
diff --git a/sys/netsmb/smb_iod.c b/sys/netsmb/smb_iod.c
new file mode 100644
index 0000000..8053303
--- /dev/null
+++ b/sys/netsmb/smb_iod.c
@@ -0,0 +1,709 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/unistd.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_tran.h>
+#include <netsmb/smb_trantcp.h>
+
+
+#define SMBIOD_SLEEP_TIMO 2
+#define SMBIOD_PING_TIMO 60 /* seconds */
+
+#define SMB_IOD_EVLOCKPTR(iod) (&((iod)->iod_evlock))
+#define SMB_IOD_EVLOCK(iod) smb_sl_lock(&((iod)->iod_evlock))
+#define SMB_IOD_EVUNLOCK(iod) smb_sl_unlock(&((iod)->iod_evlock))
+
+#define SMB_IOD_RQLOCKPTR(iod) (&((iod)->iod_rqlock))
+#define SMB_IOD_RQLOCK(iod) smb_sl_lock(&((iod)->iod_rqlock))
+#define SMB_IOD_RQUNLOCK(iod) smb_sl_unlock(&((iod)->iod_rqlock))
+
+#define smb_iod_wakeup(iod) wakeup(&(iod)->iod_flags)
+
+
+static MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon");
+
+static int smb_iod_next;
+
+static int smb_iod_sendall(struct smbiod *iod);
+static int smb_iod_disconnect(struct smbiod *iod);
+static void smb_iod_thread(void *);
+
+static __inline void
+smb_iod_rqprocessed(struct smb_rq *rqp, int error)
+{
+ SMBRQ_SLOCK(rqp);
+ rqp->sr_lerror = error;
+ rqp->sr_rpgen++;
+ rqp->sr_state = SMBRQ_NOTIFIED;
+ wakeup(&rqp->sr_state);
+ SMBRQ_SUNLOCK(rqp);
+}
+
+static void
+smb_iod_invrq(struct smbiod *iod)
+{
+ struct smb_rq *rqp;
+
+ /*
+ * Invalidate all outstanding requests for this connection
+ */
+ SMB_IOD_RQLOCK(iod);
+ TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
+ if (rqp->sr_flags & SMBR_INTERNAL)
+ SMBRQ_SUNLOCK(rqp);
+ rqp->sr_flags |= SMBR_RESTART;
+ smb_iod_rqprocessed(rqp, ENOTCONN);
+ }
+ SMB_IOD_RQUNLOCK(iod);
+}
+
+static void
+smb_iod_closetran(struct smbiod *iod)
+{
+ struct smb_vc *vcp = iod->iod_vc;
+ struct proc *p = iod->iod_p;
+
+ if (vcp->vc_tdata == NULL)
+ return;
+ SMB_TRAN_DISCONNECT(vcp, p);
+ SMB_TRAN_DONE(vcp, p);
+ vcp->vc_tdata = NULL;
+}
+
+static void
+smb_iod_dead(struct smbiod *iod)
+{
+ iod->iod_state = SMBIOD_ST_DEAD;
+ smb_iod_closetran(iod);
+ smb_iod_invrq(iod);
+}
+
+static int
+smb_iod_connect(struct smbiod *iod)
+{
+ struct smb_vc *vcp = iod->iod_vc;
+ struct proc *p = iod->iod_p;
+ int error;
+
+ SMBIODEBUG("%d\n", iod->iod_state);
+ switch(iod->iod_state) {
+ case SMBIOD_ST_VCACTIVE:
+ SMBERROR("called for already opened connection\n");
+ return EISCONN;
+ case SMBIOD_ST_DEAD:
+ return ENOTCONN; /* XXX: last error code ? */
+ default:
+ break;
+ }
+ vcp->vc_genid++;
+ error = 0;
+ itry {
+ ithrow(SMB_TRAN_CREATE(vcp, p));
+ SMBIODEBUG("tcreate\n");
+ if (vcp->vc_laddr) {
+ ithrow(SMB_TRAN_BIND(vcp, vcp->vc_laddr, p));
+ }
+ SMBIODEBUG("tbind\n");
+ ithrow(SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, p));
+ SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags);
+ iod->iod_state = SMBIOD_ST_TRANACTIVE;
+ SMBIODEBUG("tconnect\n");
+/* vcp->vc_mid = 0;*/
+ ithrow(smb_smb_negotiate(vcp, &iod->iod_scred));
+ SMBIODEBUG("snegotiate\n");
+ ithrow(smb_smb_ssnsetup(vcp, &iod->iod_scred));
+ iod->iod_state = SMBIOD_ST_VCACTIVE;
+ SMBIODEBUG("completed\n");
+ smb_iod_invrq(iod);
+ } icatch(error) {
+ smb_iod_dead(iod);
+ } ifinally {
+ } iendtry;
+ return error;
+}
+
+static int
+smb_iod_disconnect(struct smbiod *iod)
+{
+ struct smb_vc *vcp = iod->iod_vc;
+
+ SMBIODEBUG("\n");
+ if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
+ smb_smb_ssnclose(vcp, &iod->iod_scred);
+ iod->iod_state = SMBIOD_ST_TRANACTIVE;
+ }
+ vcp->vc_smbuid = SMB_UID_UNKNOWN;
+ smb_iod_closetran(iod);
+ iod->iod_state = SMBIOD_ST_NOTCONN;
+ return 0;
+}
+
+static int
+smb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp)
+{
+ int error;
+
+ if (iod->iod_state != SMBIOD_ST_VCACTIVE) {
+ if (iod->iod_state != SMBIOD_ST_DEAD)
+ return ENOTCONN;
+ iod->iod_state = SMBIOD_ST_RECONNECT;
+ error = smb_iod_connect(iod);
+ if (error)
+ return error;
+ }
+ SMBIODEBUG("tree reconnect\n");
+ SMBS_ST_LOCK(ssp);
+ ssp->ss_flags |= SMBS_RECONNECTING;
+ SMBS_ST_UNLOCK(ssp);
+ error = smb_smb_treeconnect(ssp, &iod->iod_scred);
+ SMBS_ST_LOCK(ssp);
+ ssp->ss_flags &= ~SMBS_RECONNECTING;
+ SMBS_ST_UNLOCK(ssp);
+ wakeup(&ssp->ss_vcgenid);
+ return error;
+}
+
+static int
+smb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp)
+{
+ struct proc *p = iod->iod_p;
+ struct smb_vc *vcp = iod->iod_vc;
+ struct smb_share *ssp = rqp->sr_share;
+ struct mbuf *m;
+ int error;
+
+ SMBIODEBUG("iod_state = %d\n", iod->iod_state);
+ switch (iod->iod_state) {
+ case SMBIOD_ST_NOTCONN:
+ smb_iod_rqprocessed(rqp, ENOTCONN);
+ return 0;
+ case SMBIOD_ST_DEAD:
+ iod->iod_state = SMBIOD_ST_RECONNECT;
+ return 0;
+ case SMBIOD_ST_RECONNECT:
+ return 0;
+ default:
+ break;
+ }
+ if (rqp->sr_sendcnt == 0) {
+#ifdef movedtoanotherplace
+ if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux)
+ return 0;
+#endif
+ *rqp->sr_rqtid = htoles(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
+ *rqp->sr_rquid = htoles(vcp ? vcp->vc_smbuid : 0);
+ mb_fixhdr(&rqp->sr_rq);
+ }
+ if (rqp->sr_sendcnt++ > 5) {
+ rqp->sr_flags |= SMBR_RESTART;
+ smb_iod_rqprocessed(rqp, rqp->sr_lerror);
+ /*
+ * If all attempts to send a request failed, then
+ * something is seriously hosed.
+ */
+ return ENOTCONN;
+ }
+ SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0);
+ m_dumpm(rqp->sr_rq.mb_top);
+ m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_WAIT);
+ error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, p) : ENOBUFS;
+ if (error == 0) {
+ getnanotime(&rqp->sr_timesent);
+ iod->iod_lastrqsent = rqp->sr_timesent;
+ rqp->sr_flags |= SMBR_SENT;
+ rqp->sr_state = SMBRQ_SENT;
+ return 0;
+ }
+ /*
+ * Check for fatal errors
+ */
+ if (SMB_TRAN_FATAL(vcp, error)) {
+ /*
+ * No further attempts should be made
+ */
+ return ENOTCONN;
+ }
+ if (smb_rq_intr(rqp))
+ smb_iod_rqprocessed(rqp, EINTR);
+ return 0;
+}
+
+/*
+ * Process incoming packets
+ */
+static int
+smb_iod_recvall(struct smbiod *iod)
+{
+ struct smb_vc *vcp = iod->iod_vc;
+ struct proc *p = iod->iod_p;
+ struct smb_rq *rqp;
+ struct mbuf *m;
+ u_char *hp;
+ u_short mid;
+ int error;
+
+ switch (iod->iod_state) {
+ case SMBIOD_ST_NOTCONN:
+ case SMBIOD_ST_DEAD:
+ case SMBIOD_ST_RECONNECT:
+ return 0;
+ default:
+ break;
+ }
+ for (;;) {
+ m = NULL;
+ error = SMB_TRAN_RECV(vcp, &m, p);
+ if (error == EWOULDBLOCK)
+ break;
+ if (SMB_TRAN_FATAL(vcp, error)) {
+ smb_iod_dead(iod);
+ break;
+ }
+ if (error)
+ break;
+ if (m == NULL) {
+ SMBERROR("tran return NULL without error\n");
+ error = EPIPE;
+ continue;
+ }
+ m = m_pullup(m, SMB_HDRLEN);
+ if (m == NULL)
+ continue; /* wait for a good packet */
+ /*
+ * Now we got an entire and possibly invalid SMB packet.
+ * Be careful while parsing it.
+ */
+ m_dumpm(m);
+ hp = mtod(m, u_char*);
+ if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) {
+ m_freem(m);
+ continue;
+ }
+ mid = SMB_HDRMID(hp);
+ SMBSDEBUG("mid %04x\n", (u_int)mid);
+ SMB_IOD_RQLOCK(iod);
+ TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
+ if (rqp->sr_mid != mid)
+ continue;
+ SMBRQ_SLOCK(rqp);
+ if (rqp->sr_rp.md_top == NULL) {
+ md_initm(&rqp->sr_rp, m);
+ } else {
+ if (rqp->sr_flags & SMBR_MULTIPACKET) {
+ md_append_record(&rqp->sr_rp, m);
+ } else {
+ SMBRQ_SUNLOCK(rqp);
+ SMBERROR("duplicate response %d (ignored)\n", mid);
+ break;
+ }
+ }
+ SMBRQ_SUNLOCK(rqp);
+ smb_iod_rqprocessed(rqp, 0);
+ break;
+ }
+ SMB_IOD_RQUNLOCK(iod);
+ if (rqp == NULL) {
+ SMBERROR("drop resp with mid %d\n", (u_int)mid);
+/* smb_printrqlist(vcp);*/
+ m_freem(m);
+ }
+ }
+ /*
+ * check for interrupts
+ */
+ SMB_IOD_RQLOCK(iod);
+ TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
+ if (smb_proc_intr(rqp->sr_cred->scr_p)) {
+ smb_iod_rqprocessed(rqp, EINTR);
+ }
+ }
+ SMB_IOD_RQUNLOCK(iod);
+ return 0;
+}
+
+int
+smb_iod_request(struct smbiod *iod, int event, void *ident)
+{
+ struct smbiod_event *evp;
+ int error;
+
+ SMBIODEBUG("\n");
+ evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK);
+ evp->ev_type = event;
+ evp->ev_ident = ident;
+ SMB_IOD_EVLOCK(iod);
+ STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link);
+ if ((event & SMBIOD_EV_SYNC) == 0) {
+ SMB_IOD_EVUNLOCK(iod);
+ smb_iod_wakeup(iod);
+ return 0;
+ }
+ smb_iod_wakeup(iod);
+ msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0);
+ error = evp->ev_error;
+ free(evp, M_SMBIOD);
+ return error;
+}
+
+/*
+ * Place request in the queue.
+ * Request from smbiod have a high priority.
+ */
+int
+smb_iod_addrq(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ struct smbiod *iod = vcp->vc_iod;
+ int error;
+
+ SMBIODEBUG("\n");
+ if (rqp->sr_cred->scr_p == iod->iod_p) {
+ rqp->sr_flags |= SMBR_INTERNAL;
+ SMB_IOD_RQLOCK(iod);
+ TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link);
+ SMB_IOD_RQUNLOCK(iod);
+ for (;;) {
+ if (smb_iod_sendrq(iod, rqp) != 0) {
+ smb_iod_dead(iod);
+ break;
+ }
+ /*
+ * we don't need to lock state field here
+ */
+ if (rqp->sr_state != SMBRQ_NOTSENT)
+ break;
+ tsleep(&iod->iod_flags, PWAIT, "90sndw", hz);
+ }
+ if (rqp->sr_lerror)
+ smb_iod_removerq(rqp);
+ return rqp->sr_lerror;
+ }
+
+ switch (iod->iod_state) {
+ case SMBIOD_ST_NOTCONN:
+ return ENOTCONN;
+ case SMBIOD_ST_DEAD:
+ error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
+ if (error)
+ return error;
+ return EXDEV;
+ default:
+ break;
+ }
+
+ SMB_IOD_RQLOCK(iod);
+ for (;;) {
+ if (vcp->vc_maxmux == 0) {
+ SMBERROR("maxmux == 0\n");
+ break;
+ }
+ if (iod->iod_muxcnt < vcp->vc_maxmux)
+ break;
+ iod->iod_muxwant++;
+ msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod),
+ PWAIT, "90mux", 0);
+ }
+ iod->iod_muxcnt++;
+ TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
+ SMB_IOD_RQUNLOCK(iod);
+ smb_iod_wakeup(iod);
+ return 0;
+}
+
+int
+smb_iod_removerq(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ struct smbiod *iod = vcp->vc_iod;
+
+ SMBIODEBUG("\n");
+ if (rqp->sr_flags & SMBR_INTERNAL) {
+ SMB_IOD_RQLOCK(iod);
+ TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
+ SMB_IOD_RQUNLOCK(iod);
+ return 0;
+ }
+ SMB_IOD_RQLOCK(iod);
+ while (rqp->sr_flags & SMBR_XLOCK) {
+ rqp->sr_flags |= SMBR_XLOCKWANT;
+ msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0);
+ }
+ TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
+ iod->iod_muxcnt--;
+ if (iod->iod_muxwant) {
+ iod->iod_muxwant--;
+ wakeup(&iod->iod_muxwant);
+ }
+ SMB_IOD_RQUNLOCK(iod);
+ return 0;
+}
+
+int
+smb_iod_waitrq(struct smb_rq *rqp)
+{
+ struct smbiod *iod = rqp->sr_vc->vc_iod;
+ int error;
+
+ SMBIODEBUG("\n");
+ if (rqp->sr_flags & SMBR_INTERNAL) {
+ for (;;) {
+ smb_iod_sendall(iod);
+ smb_iod_recvall(iod);
+ if (rqp->sr_rpgen != rqp->sr_rplast)
+ break;
+ tsleep(&iod->iod_flags, PWAIT, "90irq", hz);
+ }
+ smb_iod_removerq(rqp);
+ return rqp->sr_lerror;
+
+ }
+ SMBRQ_SLOCK(rqp);
+ if (rqp->sr_rpgen == rqp->sr_rplast)
+ msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0);
+ rqp->sr_rplast++;
+ SMBRQ_SUNLOCK(rqp);
+ error = rqp->sr_lerror;
+ if (rqp->sr_flags & SMBR_MULTIPACKET) {
+ /*
+ * If request should stay in the list, then reinsert it
+ * at the end of queue so other waiters have chance to concur
+ */
+ SMB_IOD_RQLOCK(iod);
+ TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
+ TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
+ SMB_IOD_RQUNLOCK(iod);
+ } else
+ smb_iod_removerq(rqp);
+ return error;
+}
+
+
+static int
+smb_iod_sendall(struct smbiod *iod)
+{
+ struct smb_vc *vcp = iod->iod_vc;
+ struct smb_rq *rqp;
+ struct timespec ts, tstimeout;
+ int herror;
+
+ herror = 0;
+ /*
+ * Loop through the list of requests and send them if possible
+ */
+ SMB_IOD_RQLOCK(iod);
+ TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
+ switch (rqp->sr_state) {
+ case SMBRQ_NOTSENT:
+ rqp->sr_flags |= SMBR_XLOCK;
+ SMB_IOD_RQUNLOCK(iod);
+ herror = smb_iod_sendrq(iod, rqp);
+ SMB_IOD_RQLOCK(iod);
+ rqp->sr_flags &= ~SMBR_XLOCK;
+ if (rqp->sr_flags & SMBR_XLOCKWANT) {
+ rqp->sr_flags &= ~SMBR_XLOCKWANT;
+ wakeup(rqp);
+ }
+ break;
+ case SMBRQ_SENT:
+ SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout);
+ timespecadd(&tstimeout, &tstimeout);
+ getnanotime(&ts);
+ timespecsub(&ts, &tstimeout);
+ if (timespeccmp(&ts, &rqp->sr_timesent, >)) {
+ smb_iod_rqprocessed(rqp, ETIMEDOUT);
+ }
+ break;
+ default:
+ }
+ if (herror)
+ break;
+ }
+ SMB_IOD_RQUNLOCK(iod);
+ if (herror == ENOTCONN)
+ smb_iod_dead(iod);
+ return 0;
+}
+
+/*
+ * "main" function for smbiod daemon
+ */
+static __inline void
+smb_iod_main(struct smbiod *iod)
+{
+/* struct smb_vc *vcp = iod->iod_vc;*/
+ struct smbiod_event *evp;
+/* struct timespec tsnow;*/
+ int error;
+
+ SMBIODEBUG("\n");
+ error = 0;
+
+ /*
+ * Check all interesting events
+ */
+ for (;;) {
+ SMB_IOD_EVLOCK(iod);
+ evp = STAILQ_FIRST(&iod->iod_evlist);
+ if (evp == NULL) {
+ SMB_IOD_EVUNLOCK(iod);
+ break;
+ }
+ STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link);
+ evp->ev_type |= SMBIOD_EV_PROCESSING;
+ SMB_IOD_EVUNLOCK(iod);
+ switch (evp->ev_type & SMBIOD_EV_MASK) {
+ case SMBIOD_EV_CONNECT:
+ iod->iod_state = SMBIOD_ST_RECONNECT;
+ evp->ev_error = smb_iod_connect(iod);
+ break;
+ case SMBIOD_EV_DISCONNECT:
+ evp->ev_error = smb_iod_disconnect(iod);
+ break;
+ case SMBIOD_EV_TREECONNECT:
+ evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident);
+ break;
+ case SMBIOD_EV_SHUTDOWN:
+ iod->iod_flags |= SMBIOD_SHUTDOWN;
+ break;
+ case SMBIOD_EV_NEWRQ:
+ break;
+ }
+ if (evp->ev_type & SMBIOD_EV_SYNC) {
+ SMB_IOD_EVLOCK(iod);
+ wakeup(evp);
+ SMB_IOD_EVUNLOCK(iod);
+ } else
+ free(evp, M_SMBIOD);
+ }
+#if 0
+ if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
+ getnanotime(&tsnow);
+ timespecsub(&tsnow, &iod->iod_pingtimo);
+ if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) {
+ smb_smb_echo(vcp, &iod->iod_scred);
+ }
+ }
+#endif
+ smb_iod_sendall(iod);
+ smb_iod_recvall(iod);
+ return;
+}
+
+#ifndef FB_CURRENT
+#define kthread_create_compat kthread_create2
+#else
+#define kthread_create_compat kthread_create
+#endif
+
+
+void
+smb_iod_thread(void *arg)
+{
+ struct smbiod *iod = arg;
+
+ mtx_lock(&Giant);
+ smb_makescred(&iod->iod_scred, iod->iod_p, NULL);
+ while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) {
+ smb_iod_main(iod);
+ SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo);
+/* mtx_unlock(&Giant, MTX_DEF);*/
+ if (iod->iod_flags & SMBIOD_SHUTDOWN)
+ break;
+ tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo);
+ }
+/* mtx_lock(&Giant, MTX_DEF);*/
+ kthread_exit(0);
+}
+
+int
+smb_iod_create(struct smb_vc *vcp)
+{
+ struct smbiod *iod;
+ int error;
+
+ iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK);
+ iod->iod_id = smb_iod_next++;
+ iod->iod_state = SMBIOD_ST_NOTCONN;
+ iod->iod_vc = vcp;
+ iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO;
+ iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO;
+ getnanotime(&iod->iod_lastrqsent);
+ vcp->vc_iod = iod;
+ smb_sl_init(&iod->iod_rqlock, "90rql");
+ TAILQ_INIT(&iod->iod_rqlist);
+ smb_sl_init(&iod->iod_evlock, "90evl");
+ STAILQ_INIT(&iod->iod_evlist);
+ error = kthread_create_compat(smb_iod_thread, iod, &iod->iod_p,
+ RFNOWAIT, "smbiod%d", iod->iod_id);
+ if (error) {
+ SMBERROR("can't start smbiod: %d", error);
+ free(iod, M_SMBIOD);
+ return error;
+ }
+ return 0;
+}
+
+int
+smb_iod_destroy(struct smbiod *iod)
+{
+ smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL);
+ mtx_destroy(&iod->iod_rqlock);
+ mtx_destroy(&iod->iod_evlock);
+ free(iod, M_SMBIOD);
+ return 0;
+}
+
+int
+smb_iod_init(void)
+{
+ return 0;
+}
+
+int
+smb_iod_done(void)
+{
+ return 0;
+}
+
diff --git a/sys/netsmb/smb_rq.c b/sys/netsmb/smb_rq.c
new file mode 100644
index 0000000..9b105bc
--- /dev/null
+++ b/sys/netsmb/smb_rq.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/mbuf.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_tran.h>
+
+MALLOC_DEFINE(M_SMBRQ, "SMBRQ", "SMB request");
+
+MODULE_DEPEND(netsmb, libmchain, 1, 1, 1);
+
+static int smb_rq_reply(struct smb_rq *rqp);
+static int smb_rq_enqueue(struct smb_rq *rqp);
+static int smb_rq_getenv(struct smb_connobj *layer,
+ struct smb_vc **vcpp, struct smb_share **sspp);
+static int smb_rq_new(struct smb_rq *rqp, u_char cmd);
+static int smb_t2_reply(struct smb_t2rq *t2p);
+
+int
+smb_rq_alloc(struct smb_connobj *layer, u_char cmd, struct smb_cred *scred,
+ struct smb_rq **rqpp)
+{
+ struct smb_rq *rqp;
+ int error;
+
+ MALLOC(rqp, struct smb_rq *, sizeof(*rqp), M_SMBRQ, M_WAITOK);
+ if (rqp == NULL)
+ return ENOMEM;
+ error = smb_rq_init(rqp, layer, cmd, scred);
+ rqp->sr_flags |= SMBR_ALLOCED;
+ if (error) {
+ smb_rq_done(rqp);
+ return error;
+ }
+ *rqpp = rqp;
+ return 0;
+}
+
+static char tzero[12];
+
+int
+smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd,
+ struct smb_cred *scred)
+{
+ int error;
+
+ bzero(rqp, sizeof(*rqp));
+ smb_sl_init(&rqp->sr_slock, "srslock");
+ error = smb_rq_getenv(layer, &rqp->sr_vc, &rqp->sr_share);
+ if (error)
+ return error;
+ error = smb_vc_access(rqp->sr_vc, scred, SMBM_EXEC);
+ if (error)
+ return error;
+ if (rqp->sr_share) {
+ error = smb_share_access(rqp->sr_share, scred, SMBM_EXEC);
+ if (error)
+ return error;
+ }
+ rqp->sr_cred = scred;
+ rqp->sr_mid = smb_vc_nextmid(rqp->sr_vc);
+ return smb_rq_new(rqp, cmd);
+}
+
+static int
+smb_rq_new(struct smb_rq *rqp, u_char cmd)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ struct mbchain *mbp = &rqp->sr_rq;
+ int error;
+
+ rqp->sr_sendcnt = 0;
+ mb_done(mbp);
+ md_done(&rqp->sr_rp);
+ error = mb_init(mbp);
+ if (error)
+ return error;
+ mb_put_mem(mbp, SMB_SIGNATURE, SMB_SIGLEN, MB_MSYSTEM);
+ mb_put_uint8(mbp, cmd);
+ mb_put_uint32le(mbp, 0); /* DosError */
+ mb_put_uint8(mbp, vcp->vc_hflags);
+ mb_put_uint16le(mbp, vcp->vc_hflags2);
+ mb_put_mem(mbp, tzero, 12, MB_MSYSTEM);
+ rqp->sr_rqtid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t));
+ mb_put_uint16le(mbp, 1 /*scred->sc_p->p_pid & 0xffff*/);
+ rqp->sr_rquid = (u_int16_t*)mb_reserve(mbp, sizeof(u_int16_t));
+ mb_put_uint16le(mbp, rqp->sr_mid);
+ return 0;
+}
+
+void
+smb_rq_done(struct smb_rq *rqp)
+{
+ mb_done(&rqp->sr_rq);
+ md_done(&rqp->sr_rp);
+ smb_sl_destroy(&rqp->sr_slock);
+ if (rqp->sr_flags & SMBR_ALLOCED)
+ free(rqp, M_SMBRQ);
+}
+
+/*
+ * Simple request-reply exchange
+ */
+int
+smb_rq_simple(struct smb_rq *rqp)
+{
+ struct smb_vc *vcp = rqp->sr_vc;
+ int error = EINVAL, i;
+
+ for (i = 0; i < SMB_MAXRCN; i++) {
+ rqp->sr_flags &= ~SMBR_RESTART;
+ rqp->sr_timo = vcp->vc_timo;
+ rqp->sr_state = SMBRQ_NOTSENT;
+ error = smb_rq_enqueue(rqp);
+ if (error)
+ return error;
+ error = smb_rq_reply(rqp);
+ if (error == 0)
+ break;
+ if ((rqp->sr_flags & (SMBR_RESTART | SMBR_NORESTART)) != SMBR_RESTART)
+ break;
+ }
+ return error;
+}
+
+static int
+smb_rq_enqueue(struct smb_rq *rqp)
+{
+ struct smb_share *ssp = rqp->sr_share;
+ int error;
+
+ if (ssp == NULL || rqp->sr_cred == &rqp->sr_vc->vc_iod->iod_scred) {
+ return smb_iod_addrq(rqp);
+ }
+ for (;;) {
+ SMBS_ST_LOCK(ssp);
+ if (ssp->ss_flags & SMBS_RECONNECTING) {
+ msleep(&ssp->ss_vcgenid, SMBS_ST_LOCKPTR(ssp),
+ PWAIT | PDROP, "90trcn", hz);
+ if (smb_proc_intr(rqp->sr_cred->scr_p))
+ return EINTR;
+ continue;
+ }
+ if (smb_share_valid(ssp) || (ssp->ss_flags & SMBS_CONNECTED) == 0) {
+ SMBS_ST_UNLOCK(ssp);
+ } else {
+ SMBS_ST_UNLOCK(ssp);
+ error = smb_iod_request(rqp->sr_vc->vc_iod,
+ SMBIOD_EV_TREECONNECT | SMBIOD_EV_SYNC, ssp);
+ if (error)
+ return error;
+ }
+ error = smb_iod_addrq(rqp);
+ if (error != EXDEV)
+ break;
+ }
+ return error;
+}
+
+void
+smb_rq_wstart(struct smb_rq *rqp)
+{
+ rqp->sr_wcount = mb_reserve(&rqp->sr_rq, sizeof(u_int8_t));
+ rqp->sr_rq.mb_count = 0;
+}
+
+void
+smb_rq_wend(struct smb_rq *rqp)
+{
+ if (rqp->sr_wcount == NULL) {
+ SMBERROR("no wcount\n"); /* actually panic */
+ return;
+ }
+ if (rqp->sr_rq.mb_count & 1)
+ SMBERROR("odd word count\n");
+ *rqp->sr_wcount = rqp->sr_rq.mb_count / 2;
+}
+
+void
+smb_rq_bstart(struct smb_rq *rqp)
+{
+ rqp->sr_bcount = (u_short*)mb_reserve(&rqp->sr_rq, sizeof(u_short));
+ rqp->sr_rq.mb_count = 0;
+}
+
+void
+smb_rq_bend(struct smb_rq *rqp)
+{
+ int bcnt;
+
+ if (rqp->sr_bcount == NULL) {
+ SMBERROR("no bcount\n"); /* actually panic */
+ return;
+ }
+ bcnt = rqp->sr_rq.mb_count;
+ if (bcnt > 0xffff)
+ SMBERROR("byte count too large (%d)\n", bcnt);
+ *rqp->sr_bcount = bcnt;
+}
+
+int
+smb_rq_intr(struct smb_rq *rqp)
+{
+ struct proc *p = rqp->sr_cred->scr_p;
+
+ if (rqp->sr_flags & SMBR_INTR)
+ return EINTR;
+ return smb_proc_intr(p);
+}
+
+int
+smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp)
+{
+ *mbpp = &rqp->sr_rq;
+ return 0;
+}
+
+int
+smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp)
+{
+ *mbpp = &rqp->sr_rp;
+ return 0;
+}
+
+static int
+smb_rq_getenv(struct smb_connobj *layer,
+ struct smb_vc **vcpp, struct smb_share **sspp)
+{
+ struct smb_vc *vcp = NULL;
+ struct smb_share *ssp = NULL;
+ struct smb_connobj *cp;
+ int error = 0;
+
+ switch (layer->co_level) {
+ case SMBL_VC:
+ vcp = CPTOVC(layer);
+ if (layer->co_parent == NULL) {
+ SMBERROR("zombie VC %s\n", vcp->vc_srvname);
+ error = EINVAL;
+ break;
+ }
+ break;
+ case SMBL_SHARE:
+ ssp = CPTOSS(layer);
+ cp = layer->co_parent;
+ if (cp == NULL) {
+ SMBERROR("zombie share %s\n", ssp->ss_name);
+ error = EINVAL;
+ break;
+ }
+ error = smb_rq_getenv(cp, &vcp, NULL);
+ if (error)
+ break;
+ break;
+ default:
+ SMBERROR("invalid layer %d passed\n", layer->co_level);
+ error = EINVAL;
+ }
+ if (vcpp)
+ *vcpp = vcp;
+ if (sspp)
+ *sspp = ssp;
+ return error;
+}
+
+/*
+ * Wait for reply on the request
+ */
+static int
+smb_rq_reply(struct smb_rq *rqp)
+{
+ struct mdchain *mdp = &rqp->sr_rp;
+ u_int32_t tdw;
+ u_int8_t tb;
+ int error, rperror = 0;
+
+ error = smb_iod_waitrq(rqp);
+ if (error)
+ return error;
+ error = md_get_uint32(mdp, &tdw);
+ if (error)
+ return error;
+ error = md_get_uint8(mdp, &tb);
+ if (rqp->sr_vc->vc_hflags2 & SMB_FLAGS2_ERR_STATUS) {
+ error = md_get_uint32le(mdp, &rqp->sr_error);
+ } else {
+ error = md_get_uint8(mdp, &rqp->sr_errclass);
+ error = md_get_uint8(mdp, &tb);
+ error = md_get_uint16le(mdp, &rqp->sr_serror);
+ if (!error)
+ rperror = smb_maperror(rqp->sr_errclass, rqp->sr_serror);
+ }
+ error = md_get_uint8(mdp, &rqp->sr_rpflags);
+ error = md_get_uint16le(mdp, &rqp->sr_rpflags2);
+
+ error = md_get_uint32(mdp, &tdw);
+ error = md_get_uint32(mdp, &tdw);
+ error = md_get_uint32(mdp, &tdw);
+
+ error = md_get_uint16le(mdp, &rqp->sr_rptid);
+ error = md_get_uint16le(mdp, &rqp->sr_rppid);
+ error = md_get_uint16le(mdp, &rqp->sr_rpuid);
+ error = md_get_uint16le(mdp, &rqp->sr_rpmid);
+
+ SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x, E: %d:%d\n",
+ rqp->sr_rpmid, rqp->sr_rppid, rqp->sr_rpuid, rqp->sr_rptid,
+ rqp->sr_errclass, rqp->sr_serror);
+ return error ? error : rperror;
+}
+
+
+#define ALIGN4(a) (((a) + 3) & ~3)
+
+/*
+ * TRANS2 request implementation
+ */
+int
+smb_t2_alloc(struct smb_connobj *layer, u_short setup, struct smb_cred *scred,
+ struct smb_t2rq **t2pp)
+{
+ struct smb_t2rq *t2p;
+ int error;
+
+ MALLOC(t2p, struct smb_t2rq *, sizeof(*t2p), M_SMBRQ, M_WAITOK);
+ if (t2p == NULL)
+ return ENOMEM;
+ error = smb_t2_init(t2p, layer, setup, scred);
+ t2p->t2_flags |= SMBT2_ALLOCED;
+ if (error) {
+ smb_t2_done(t2p);
+ return error;
+ }
+ *t2pp = t2p;
+ return 0;
+}
+
+int
+smb_t2_init(struct smb_t2rq *t2p, struct smb_connobj *source, u_short setup,
+ struct smb_cred *scred)
+{
+ int error;
+
+ bzero(t2p, sizeof(*t2p));
+ t2p->t2_source = source;
+ t2p->t2_setupcount = 1;
+ t2p->t2_setupdata = t2p->t2_setup;
+ t2p->t2_setup[0] = setup;
+ t2p->t2_fid = 0xffff;
+ t2p->t2_cred = scred;
+ error = smb_rq_getenv(source, &t2p->t2_vc, NULL);
+ if (error)
+ return error;
+ return 0;
+}
+
+void
+smb_t2_done(struct smb_t2rq *t2p)
+{
+ mb_done(&t2p->t2_tparam);
+ mb_done(&t2p->t2_tdata);
+ md_done(&t2p->t2_rparam);
+ md_done(&t2p->t2_rdata);
+ if (t2p->t2_flags & SMBT2_ALLOCED)
+ free(t2p, M_SMBRQ);
+}
+
+static int
+smb_t2_placedata(struct mbuf *mtop, u_int16_t offset, u_int16_t count,
+ struct mdchain *mdp)
+{
+ struct mbuf *m, *m0;
+ int len;
+
+ m0 = m_split(mtop, offset, M_WAIT);
+ if (m0 == NULL)
+ return EBADRPC;
+ for(len = 0, m = m0; m->m_next; m = m->m_next)
+ len += m->m_len;
+ len += m->m_len;
+ m->m_len -= len - count;
+ if (mdp->md_top == NULL) {
+ md_initm(mdp, m0);
+ } else
+ m_cat(mdp->md_top, m0);
+ return 0;
+}
+
+static int
+smb_t2_reply(struct smb_t2rq *t2p)
+{
+ struct mdchain *mdp;
+ struct smb_rq *rqp = t2p->t2_rq;
+ int error, totpgot, totdgot;
+ u_int16_t totpcount, totdcount, pcount, poff, doff, pdisp, ddisp;
+ u_int16_t tmp, bc, dcount;
+ u_int8_t wc;
+
+ error = smb_rq_reply(rqp);
+ if (error)
+ return error;
+ if ((t2p->t2_flags & SMBT2_ALLSENT) == 0) {
+ /*
+ * this is an interim response, ignore it.
+ */
+ SMBRQ_SLOCK(rqp);
+ md_next_record(&rqp->sr_rp);
+ SMBRQ_SUNLOCK(rqp);
+ return 0;
+ }
+ /*
+ * Now we have to get all subseqent responses. The CIFS specification
+ * says that they can be misordered which is weird.
+ * TODO: timo
+ */
+ totpgot = totdgot = 0;
+ totpcount = totdcount = 0xffff;
+ mdp = &rqp->sr_rp;
+ for (;;) {
+ m_dumpm(mdp->md_top);
+ if ((error = md_get_uint8(mdp, &wc)) != 0)
+ break;
+ if (wc < 10) {
+ error = ENOENT;
+ break;
+ }
+ if ((error = md_get_uint16le(mdp, &tmp)) != 0)
+ break;
+ if (totpcount > tmp)
+ totpcount = tmp;
+ md_get_uint16le(mdp, &tmp);
+ if (totdcount > tmp)
+ totdcount = tmp;
+ if ((error = md_get_uint16le(mdp, &tmp)) != 0 || /* reserved */
+ (error = md_get_uint16le(mdp, &pcount)) != 0 ||
+ (error = md_get_uint16le(mdp, &poff)) != 0 ||
+ (error = md_get_uint16le(mdp, &pdisp)) != 0)
+ break;
+ if (pcount != 0 && pdisp != totpgot) {
+ SMBERROR("Can't handle misordered parameters %d:%d\n",
+ pdisp, totpgot);
+ error = EINVAL;
+ break;
+ }
+ if ((error = md_get_uint16le(mdp, &dcount)) != 0 ||
+ (error = md_get_uint16le(mdp, &doff)) != 0 ||
+ (error = md_get_uint16le(mdp, &ddisp)) != 0)
+ break;
+ if (dcount != 0 && ddisp != totdgot) {
+ SMBERROR("Can't handle misordered data\n");
+ error = EINVAL;
+ break;
+ }
+ md_get_uint8(mdp, &wc);
+ md_get_uint8(mdp, NULL);
+ tmp = wc;
+ while (tmp--)
+ md_get_uint16(mdp, NULL);
+ if ((error = md_get_uint16le(mdp, &bc)) != 0)
+ break;
+/* tmp = SMB_HDRLEN + 1 + 10 * 2 + 2 * wc + 2;*/
+ if (dcount) {
+ error = smb_t2_placedata(mdp->md_top, doff, dcount,
+ &t2p->t2_rdata);
+ if (error)
+ break;
+ }
+ if (pcount) {
+ error = smb_t2_placedata(mdp->md_top, poff, pcount,
+ &t2p->t2_rparam);
+ if (error)
+ break;
+ }
+ totpgot += pcount;
+ totdgot += dcount;
+ if (totpgot >= totpcount && totdgot >= totdcount) {
+ error = 0;
+ t2p->t2_flags |= SMBT2_ALLRECV;
+ break;
+ }
+ /*
+ * We're done with this reply, look for the next one.
+ */
+ SMBRQ_SLOCK(rqp);
+ md_next_record(&rqp->sr_rp);
+ SMBRQ_SUNLOCK(rqp);
+ error = smb_rq_reply(rqp);
+ if (error)
+ break;
+ }
+ return error;
+}
+
+/*
+ * Perform a full round of TRANS2 request
+ */
+static int
+smb_t2_request_int(struct smb_t2rq *t2p)
+{
+ struct smb_vc *vcp = t2p->t2_vc;
+ struct smb_cred *scred = t2p->t2_cred;
+ struct mbchain *mbp;
+ struct mdchain *mdp, mbparam, mbdata;
+ struct mbuf *m;
+ struct smb_rq *rqp;
+ int totpcount, leftpcount, totdcount, leftdcount, len, txmax, i;
+ int error, doff, poff, txdcount, txpcount, nmlen;
+
+ m = t2p->t2_tparam.mb_top;
+ if (m) {
+ md_initm(&mbparam, m); /* do not free it! */
+ totpcount = m_fixhdr(m);
+ if (totpcount > 0xffff) /* maxvalue for u_short */
+ return EINVAL;
+ } else
+ totpcount = 0;
+ m = t2p->t2_tdata.mb_top;
+ if (m) {
+ md_initm(&mbdata, m); /* do not free it! */
+ totdcount = m_fixhdr(m);
+ if (totdcount > 0xffff)
+ return EINVAL;
+ } else
+ totdcount = 0;
+ leftdcount = totdcount;
+ leftpcount = totpcount;
+ txmax = vcp->vc_txmax;
+ error = smb_rq_alloc(t2p->t2_source, t2p->t_name ?
+ SMB_COM_TRANSACTION : SMB_COM_TRANSACTION2, scred, &rqp);
+ if (error)
+ return error;
+ rqp->sr_flags |= SMBR_MULTIPACKET;
+ t2p->t2_rq = rqp;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, totpcount);
+ mb_put_uint16le(mbp, totdcount);
+ mb_put_uint16le(mbp, t2p->t2_maxpcount);
+ mb_put_uint16le(mbp, t2p->t2_maxdcount);
+ mb_put_uint8(mbp, t2p->t2_maxscount);
+ mb_put_uint8(mbp, 0); /* reserved */
+ mb_put_uint16le(mbp, 0); /* flags */
+ mb_put_uint32le(mbp, 0); /* Timeout */
+ mb_put_uint16le(mbp, 0); /* reserved 2 */
+ len = mb_fixhdr(mbp);
+ /*
+ * now we have known packet size as
+ * ALIGN4(len + 5 * 2 + setupcount * 2 + 2 + strlen(name) + 1),
+ * and need to decide which parts should go into the first request
+ */
+ nmlen = t2p->t_name ? strlen(t2p->t_name) : 0;
+ len = ALIGN4(len + 5 * 2 + t2p->t2_setupcount * 2 + 2 + nmlen + 1);
+ if (len + leftpcount > txmax) {
+ txpcount = min(leftpcount, txmax - len);
+ poff = len;
+ txdcount = 0;
+ doff = 0;
+ } else {
+ txpcount = leftpcount;
+ poff = txpcount ? len : 0;
+ len = ALIGN4(len + txpcount);
+ txdcount = min(leftdcount, txmax - len);
+ doff = txdcount ? len : 0;
+ }
+ leftpcount -= txpcount;
+ leftdcount -= txdcount;
+ mb_put_uint16le(mbp, txpcount);
+ mb_put_uint16le(mbp, poff);
+ mb_put_uint16le(mbp, txdcount);
+ mb_put_uint16le(mbp, doff);
+ mb_put_uint8(mbp, t2p->t2_setupcount);
+ mb_put_uint8(mbp, 0);
+ for (i = 0; i < t2p->t2_setupcount; i++)
+ mb_put_uint16le(mbp, t2p->t2_setupdata[i]);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ /* TDUNICODE */
+ if (t2p->t_name)
+ mb_put_mem(mbp, t2p->t_name, nmlen, MB_MSYSTEM);
+ mb_put_uint8(mbp, 0); /* terminating zero */
+ len = mb_fixhdr(mbp);
+ if (txpcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbparam, txpcount, &m);
+ SMBSDEBUG("%d:%d:%d\n", error, txpcount, txmax);
+ if (error)
+ goto freerq;
+ mb_put_mbuf(mbp, m);
+ }
+ len = mb_fixhdr(mbp);
+ if (txdcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbdata, txdcount, &m);
+ if (error)
+ goto freerq;
+ mb_put_mbuf(mbp, m);
+ }
+ smb_rq_bend(rqp); /* incredible, but thats it... */
+ error = smb_rq_enqueue(rqp);
+ if (error)
+ goto freerq;
+ if (leftpcount == 0 && leftdcount == 0)
+ t2p->t2_flags |= SMBT2_ALLSENT;
+ error = smb_t2_reply(t2p);
+ if (error)
+ goto bad;
+ while (leftpcount || leftdcount) {
+ error = smb_rq_new(rqp, t2p->t_name ?
+ SMB_COM_TRANSACTION_SECONDARY : SMB_COM_TRANSACTION2_SECONDARY);
+ if (error)
+ goto bad;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, totpcount);
+ mb_put_uint16le(mbp, totdcount);
+ len = mb_fixhdr(mbp);
+ /*
+ * now we have known packet size as
+ * ALIGN4(len + 7 * 2 + 2) for T2 request, and -2 for T one,
+ * and need to decide which parts should go into request
+ */
+ len = ALIGN4(len + 6 * 2 + 2);
+ if (t2p->t_name == NULL)
+ len += 2;
+ if (len + leftpcount > txmax) {
+ txpcount = min(leftpcount, txmax - len);
+ poff = len;
+ txdcount = 0;
+ doff = 0;
+ } else {
+ txpcount = leftpcount;
+ poff = txpcount ? len : 0;
+ len = ALIGN4(len + txpcount);
+ txdcount = min(leftdcount, txmax - len);
+ doff = txdcount ? len : 0;
+ }
+ mb_put_uint16le(mbp, txpcount);
+ mb_put_uint16le(mbp, poff);
+ mb_put_uint16le(mbp, totpcount - leftpcount);
+ mb_put_uint16le(mbp, txdcount);
+ mb_put_uint16le(mbp, doff);
+ mb_put_uint16le(mbp, totdcount - leftdcount);
+ leftpcount -= txpcount;
+ leftdcount -= txdcount;
+ if (t2p->t_name == NULL)
+ mb_put_uint16le(mbp, t2p->t2_fid);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, 0); /* name */
+ len = mb_fixhdr(mbp);
+ if (txpcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbparam, txpcount, &m);
+ if (error)
+ goto bad;
+ mb_put_mbuf(mbp, m);
+ }
+ len = mb_fixhdr(mbp);
+ if (txdcount) {
+ mb_put_mem(mbp, NULL, ALIGN4(len) - len, MB_MZERO);
+ error = md_get_mbuf(&mbdata, txdcount, &m);
+ if (error)
+ goto bad;
+ mb_put_mbuf(mbp, m);
+ }
+ smb_rq_bend(rqp);
+ rqp->sr_state = SMBRQ_NOTSENT;
+ error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_NEWRQ, NULL);
+ if (error)
+ goto bad;
+ } /* while left params or data */
+ t2p->t2_flags |= SMBT2_ALLSENT;
+ mdp = &t2p->t2_rdata;
+ if (mdp->md_top) {
+ m_fixhdr(mdp->md_top);
+ md_initm(mdp, mdp->md_top);
+ }
+ mdp = &t2p->t2_rparam;
+ if (mdp->md_top) {
+ m_fixhdr(mdp->md_top);
+ md_initm(mdp, mdp->md_top);
+ }
+bad:
+ smb_iod_removerq(rqp);
+freerq:
+ smb_rq_done(rqp);
+ if (error) {
+ if (rqp->sr_flags & SMBR_RESTART)
+ t2p->t2_flags |= SMBT2_RESTART;
+ md_done(&t2p->t2_rparam);
+ md_done(&t2p->t2_rdata);
+ }
+ return error;
+}
+
+int
+smb_t2_request(struct smb_t2rq *t2p)
+{
+ int error = EINVAL, i;
+
+ for (i = 0; i < SMB_MAXRCN; i++) {
+ t2p->t2_flags &= ~SMBR_RESTART;
+ error = smb_t2_request_int(t2p);
+ if (error == 0)
+ break;
+ if ((t2p->t2_flags & (SMBT2_RESTART | SMBT2_NORESTART)) != SMBT2_RESTART)
+ break;
+ }
+ return error;
+}
diff --git a/sys/netsmb/smb_rq.h b/sys/netsmb/smb_rq.h
new file mode 100644
index 0000000..d37647b
--- /dev/null
+++ b/sys/netsmb/smb_rq.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NETSMB_SMB_RQ_H_
+#define _NETSMB_SMB_RQ_H_
+
+#ifndef MB_MSYSTEM
+#include <sys/mchain.h>
+#endif
+
+#define SMBR_ALLOCED 0x0001 /* structure was malloced */
+#define SMBR_SENT 0x0002 /* request successfully transmitted */
+#define SMBR_REXMIT 0x0004 /* request should be retransmitted */
+#define SMBR_INTR 0x0008 /* request interrupted */
+#define SMBR_RESTART 0x0010 /* request should be repeated if possible */
+#define SMBR_NORESTART 0x0020 /* request is not restartable */
+#define SMBR_MULTIPACKET 0x0040 /* multiple packets can be sent and received */
+#define SMBR_INTERNAL 0x0080 /* request is internal to smbrqd */
+#define SMBR_XLOCK 0x0100 /* request locked and can't be moved */
+#define SMBR_XLOCKWANT 0x0200 /* waiter on XLOCK */
+
+#define SMBT2_ALLSENT 0x0001 /* all data and params are sent */
+#define SMBT2_ALLRECV 0x0002 /* all data and params are received */
+#define SMBT2_ALLOCED 0x0004
+#define SMBT2_RESTART 0x0008
+#define SMBT2_NORESTART 0x0010
+
+#define SMBRQ_SLOCK(rqp) smb_sl_lock(&(rqp)->sr_slock)
+#define SMBRQ_SUNLOCK(rqp) smb_sl_unlock(&(rqp)->sr_slock)
+#define SMBRQ_SLOCKPTR(rqp) (&(rqp)->sr_slock)
+
+
+enum smbrq_state {
+ SMBRQ_NOTSENT, /* rq have data to send */
+ SMBRQ_SENT, /* send procedure completed */
+ SMBRQ_REPLYRECEIVED,
+ SMBRQ_NOTIFIED /* owner notified about completion */
+};
+
+struct smb_vc;
+struct smb_t2rq;
+
+struct smb_rq {
+ enum smbrq_state sr_state;
+ struct smb_vc * sr_vc;
+ struct smb_share* sr_share;
+ u_short sr_mid;
+ struct mbchain sr_rq;
+ u_int8_t sr_rqflags;
+ u_int16_t sr_rqflags2;
+ u_char * sr_wcount;
+ u_short * sr_bcount;
+ struct mdchain sr_rp;
+ int sr_rpgen;
+ int sr_rplast;
+ int sr_flags; /* SMBR_* */
+ int sr_rpsize;
+ struct smb_cred * sr_cred;
+ int sr_timo;
+ int sr_rexmit;
+ int sr_sendcnt;
+ struct timespec sr_timesent;
+ int sr_lerror;
+ u_int16_t * sr_rqtid;
+ u_int16_t * sr_rquid;
+ u_int8_t sr_errclass;
+ u_int16_t sr_serror;
+ u_int32_t sr_error;
+ u_int8_t sr_rpflags;
+ u_int16_t sr_rpflags2;
+ u_int16_t sr_rptid;
+ u_int16_t sr_rppid;
+ u_int16_t sr_rpuid;
+ u_int16_t sr_rpmid;
+ struct smb_slock sr_slock; /* short term locks */
+/* struct smb_t2rq*sr_t2;*/
+ TAILQ_ENTRY(smb_rq) sr_link;
+};
+
+struct smb_t2rq {
+ u_int16_t t2_setupcount;
+ u_int16_t * t2_setupdata;
+ u_int16_t t2_setup[2]; /* most of rqs has setupcount of 1 */
+ u_int8_t t2_maxscount; /* max setup words to return */
+ u_int16_t t2_maxpcount; /* max param bytes to return */
+ u_int16_t t2_maxdcount; /* max data bytes to return */
+ u_int16_t t2_fid; /* for T2 request */
+ char * t_name; /* for T request, should be zero for T2 */
+ int t2_flags; /* SMBT2_ */
+ struct mbchain t2_tparam; /* parameters to transmit */
+ struct mbchain t2_tdata; /* data to transmit */
+ struct mdchain t2_rparam; /* received paramters */
+ struct mdchain t2_rdata; /* received data */
+ struct smb_cred*t2_cred;
+ struct smb_connobj *t2_source;
+ struct smb_rq * t2_rq;
+ struct smb_vc * t2_vc;
+};
+
+int smb_rq_alloc(struct smb_connobj *layer, u_char cmd,
+ struct smb_cred *scred, struct smb_rq **rqpp);
+int smb_rq_init(struct smb_rq *rqp, struct smb_connobj *layer, u_char cmd,
+ struct smb_cred *scred);
+void smb_rq_done(struct smb_rq *rqp);
+int smb_rq_getrequest(struct smb_rq *rqp, struct mbchain **mbpp);
+int smb_rq_getreply(struct smb_rq *rqp, struct mdchain **mbpp);
+void smb_rq_wstart(struct smb_rq *rqp);
+void smb_rq_wend(struct smb_rq *rqp);
+void smb_rq_bstart(struct smb_rq *rqp);
+void smb_rq_bend(struct smb_rq *rqp);
+int smb_rq_intr(struct smb_rq *rqp);
+int smb_rq_simple(struct smb_rq *rqp);
+
+int smb_t2_alloc(struct smb_connobj *layer, u_short setup, struct smb_cred *scred,
+ struct smb_t2rq **rqpp);
+int smb_t2_init(struct smb_t2rq *rqp, struct smb_connobj *layer, u_short setup,
+ struct smb_cred *scred);
+void smb_t2_done(struct smb_t2rq *t2p);
+int smb_t2_request(struct smb_t2rq *t2p);
+
+#endif /* !_NETSMB_SMB_RQ_H_ */
diff --git a/sys/netsmb/smb_smb.c b/sys/netsmb/smb_smb.c
new file mode 100644
index 0000000..0f3179d
--- /dev/null
+++ b/sys/netsmb/smb_smb.c
@@ -0,0 +1,660 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+/*
+ * various SMB requests. Most of the routines merely packs data into mbufs.
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <sys/iconv.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_tran.h>
+
+struct smb_dialect {
+ int d_id;
+ const char * d_name;
+};
+
+static struct smb_dialect smb_dialects[] = {
+ {SMB_DIALECT_CORE, "PC NETWORK PROGRAM 1.0"},
+ {SMB_DIALECT_COREPLUS, "MICROSOFT NETWORKS 1.03"},
+ {SMB_DIALECT_LANMAN1_0, "MICROSOFT NETWORKS 3.0"},
+ {SMB_DIALECT_LANMAN1_0, "LANMAN1.0"},
+ {SMB_DIALECT_LANMAN2_0, "LM1.2X002"},
+ {SMB_DIALECT_LANMAN2_0, "Samba"},
+ {SMB_DIALECT_NTLM0_12, "NT LANMAN 1.0"},
+ {SMB_DIALECT_NTLM0_12, "NT LM 0.12"},
+ {-1, NULL}
+};
+
+#define SMB_DIALECT_MAX (sizeof(smb_dialects) / sizeof(struct smb_dialect) - 2)
+
+static int
+smb_smb_nomux(struct smb_vc *vcp, struct smb_cred *scred, const char *name)
+{
+ if (scred->scr_p == vcp->vc_iod->iod_p)
+ return 0;
+ SMBERROR("wrong function called(%s)\n", name);
+ return EINVAL;
+}
+
+int
+smb_smb_negotiate(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ struct smb_dialect *dp;
+ struct smb_sopt *sp = NULL;
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int8_t wc, stime[8], sblen;
+ u_int16_t dindex, tw, tw1, swlen, bc;
+ int error, maxqsz;
+
+ if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0)
+ return EINVAL;
+ vcp->vc_hflags = 0;
+ vcp->vc_hflags2 = 0;
+ vcp->obj.co_flags &= ~(SMBV_ENCRYPT);
+ sp = &vcp->vc_sopt;
+ bzero(sp, sizeof(struct smb_sopt));
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_NEGOTIATE, scred, &rqp);
+ if (error)
+ return error;
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ for(dp = smb_dialects; dp->d_id != -1; dp++) {
+ mb_put_uint8(mbp, SMB_DT_DIALECT);
+ smb_put_dstring(mbp, vcp, dp->d_name, SMB_CS_NONE);
+ }
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ if (error)
+ goto bad;
+ smb_rq_getreply(rqp, &mdp);
+ do {
+ error = md_get_uint8(mdp, &wc);
+ if (error)
+ break;
+ error = md_get_uint16le(mdp, &dindex);
+ if (error)
+ break;
+ if (dindex > 7) {
+ SMBERROR("Don't know how to talk with server %s (%d)\n", "xxx", dindex);
+ error = EBADRPC;
+ break;
+ }
+ dp = smb_dialects + dindex;
+ sp->sv_proto = dp->d_id;
+ SMBSDEBUG("Dialect %s (%d, %d)\n", dp->d_name, dindex, wc);
+ error = EBADRPC;
+ if (dp->d_id >= SMB_DIALECT_NTLM0_12) {
+ if (wc != 17)
+ break;
+ md_get_uint8(mdp, &sp->sv_sm);
+ md_get_uint16le(mdp, &sp->sv_maxmux);
+ md_get_uint16le(mdp, &sp->sv_maxvcs);
+ md_get_uint32le(mdp, &sp->sv_maxtx);
+ md_get_uint32le(mdp, &sp->sv_maxraw);
+ md_get_uint32le(mdp, &sp->sv_skey);
+ md_get_uint32le(mdp, &sp->sv_caps);
+ md_get_mem(mdp, stime, 8, MB_MSYSTEM);
+ md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
+ md_get_uint8(mdp, &sblen);
+ if (sblen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
+ if (sblen != SMB_MAXCHALLENGELEN) {
+ SMBERROR("Unexpected length of security blob (%d)\n", sblen);
+ break;
+ }
+ error = md_get_uint16(mdp, &bc);
+ if (error)
+ break;
+ if (sp->sv_caps & SMB_CAP_EXT_SECURITY)
+ md_get_mem(mdp, NULL, 16, MB_MSYSTEM);
+ error = md_get_mem(mdp, vcp->vc_ch, sblen, MB_MSYSTEM);
+ if (error)
+ break;
+ vcp->vc_chlen = sblen;
+ vcp->obj.co_flags |= SMBV_ENCRYPT;
+ }
+ vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
+ if (dp->d_id == SMB_DIALECT_NTLM0_12 &&
+ sp->sv_maxtx < 4096 &&
+ (sp->sv_caps & SMB_CAP_NT_SMBS) == 0) {
+ vcp->obj.co_flags |= SMBV_WIN95;
+ SMBSDEBUG("Win95 detected\n");
+ }
+ } else if (dp->d_id > SMB_DIALECT_CORE) {
+ md_get_uint16le(mdp, &tw);
+ sp->sv_sm = tw;
+ md_get_uint16le(mdp, &tw);
+ sp->sv_maxtx = tw;
+ md_get_uint16le(mdp, &sp->sv_maxmux);
+ md_get_uint16le(mdp, &sp->sv_maxvcs);
+ md_get_uint16le(mdp, &tw); /* rawmode */
+ md_get_uint32le(mdp, &sp->sv_skey);
+ if (wc == 13) { /* >= LANMAN1 */
+ md_get_uint16(mdp, &tw); /* time */
+ md_get_uint16(mdp, &tw1); /* date */
+ md_get_uint16le(mdp, (u_int16_t*)&sp->sv_tz);
+ md_get_uint16le(mdp, &swlen);
+ if (swlen > SMB_MAXCHALLENGELEN)
+ break;
+ md_get_uint16(mdp, NULL); /* mbz */
+ if (md_get_uint16(mdp, &bc) != 0)
+ break;
+ if (bc < swlen)
+ break;
+ if (swlen && (sp->sv_sm & SMB_SM_ENCRYPT)) {
+ error = md_get_mem(mdp, vcp->vc_ch, swlen, MB_MSYSTEM);
+ if (error)
+ break;
+ vcp->vc_chlen = swlen;
+ vcp->obj.co_flags |= SMBV_ENCRYPT;
+ }
+ }
+ vcp->vc_hflags2 |= SMB_FLAGS2_KNOWS_LONG_NAMES;
+ } else { /* an old CORE protocol */
+ sp->sv_maxmux = 1;
+ }
+ error = 0;
+ } while (0);
+ if (error == 0) {
+ vcp->vc_maxvcs = sp->sv_maxvcs;
+ if (vcp->vc_maxvcs <= 1) {
+ if (vcp->vc_maxvcs == 0)
+ vcp->vc_maxvcs = 1;
+ }
+ if (sp->sv_maxtx <= 0 || sp->sv_maxtx > 0xffff)
+ sp->sv_maxtx = 1024;
+ SMB_TRAN_GETPARAM(vcp, SMBTP_SNDSZ, &maxqsz);
+ vcp->vc_txmax = min(sp->sv_maxtx, maxqsz);
+ SMBSDEBUG("TZ = %d\n", sp->sv_tz);
+ SMBSDEBUG("CAPS = %x\n", sp->sv_caps);
+ SMBSDEBUG("MAXMUX = %d\n", sp->sv_maxmux);
+ SMBSDEBUG("MAXVCS = %d\n", sp->sv_maxvcs);
+ SMBSDEBUG("MAXRAW = %d\n", sp->sv_maxraw);
+ SMBSDEBUG("MAXTX = %d\n", sp->sv_maxtx);
+ }
+bad:
+ smb_rq_done(rqp);
+ return error;
+}
+
+int
+smb_smb_ssnsetup(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+/* u_int8_t wc;
+ u_int16_t tw, tw1;*/
+ smb_uniptr unipp, ntencpass = NULL;
+ char *pp, *up, *pbuf, *encpass;
+ int error, plen, uniplen, ulen;
+
+ vcp->vc_smbuid = SMB_UID_UNKNOWN;
+
+ if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0)
+ return EINVAL;
+
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_SESSION_SETUP_ANDX, scred, &rqp);
+ if (error)
+ return error;
+ pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
+ encpass = malloc(24, M_SMBTEMP, M_WAITOK);
+ if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
+ iconv_convstr(vcp->vc_toupper, pbuf, smb_vc_getpass(vcp));
+ iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
+ if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
+ uniplen = plen = 24;
+ smb_encrypt(pbuf, vcp->vc_ch, encpass);
+ ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
+ iconv_convstr(vcp->vc_toserver, pbuf, smb_vc_getpass(vcp));
+ smb_ntencrypt(pbuf, vcp->vc_ch, (u_char*)ntencpass);
+ pp = encpass;
+ unipp = ntencpass;
+ } else {
+ plen = strlen(pbuf) + 1;
+ pp = pbuf;
+ uniplen = plen * 2;
+ ntencpass = malloc(uniplen, M_SMBTEMP, M_WAITOK);
+ smb_strtouni(ntencpass, smb_vc_getpass(vcp));
+ plen--;
+ uniplen = 0/*-= 2*/;
+ unipp = ntencpass;
+ }
+ } else {
+ /*
+ * In the share security mode password will be used
+ * only in the tree authentication
+ */
+ pp = "";
+ plen = 1;
+ unipp = &smb_unieol;
+ uniplen = sizeof(smb_unieol);
+ }
+ smb_rq_wstart(rqp);
+ mbp = &rqp->sr_rq;
+ up = vcp->vc_username;
+ ulen = strlen(up) + 1;
+ mb_put_uint8(mbp, 0xff);
+ mb_put_uint8(mbp, 0);
+ mb_put_uint16le(mbp, 0);
+ mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxtx);
+ mb_put_uint16le(mbp, vcp->vc_sopt.sv_maxmux);
+ mb_put_uint16le(mbp, vcp->vc_number);
+ mb_put_uint32le(mbp, vcp->vc_sopt.sv_skey);
+ mb_put_uint16le(mbp, plen);
+ if (SMB_DIALECT(vcp) < SMB_DIALECT_NTLM0_12) {
+ mb_put_uint32le(mbp, 0);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
+ smb_put_dstring(mbp, vcp, up, SMB_CS_NONE);
+ } else {
+ mb_put_uint16le(mbp, uniplen);
+ mb_put_uint32le(mbp, 0); /* reserved */
+ mb_put_uint32le(mbp, 0); /* my caps */
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
+ mb_put_mem(mbp, (caddr_t)unipp, uniplen, MB_MSYSTEM);
+ smb_put_dstring(mbp, vcp, up, SMB_CS_NONE); /* AccountName */
+ smb_put_dstring(mbp, vcp, vcp->vc_domain, SMB_CS_NONE); /* PrimaryDomain */
+ smb_put_dstring(mbp, vcp, "FreeBSD", SMB_CS_NONE); /* Client's OS */
+ smb_put_dstring(mbp, vcp, "NETSMB", SMB_CS_NONE); /* Client name */
+ }
+ smb_rq_bend(rqp);
+ if (ntencpass)
+ free(ntencpass, M_SMBTEMP);
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ if (error) {
+ if (rqp->sr_errclass == ERRDOS && rqp->sr_serror == ERRnoaccess)
+ error = EAUTH;
+ goto bad;
+ }
+ vcp->vc_smbuid = rqp->sr_rpuid;
+bad:
+ free(encpass, M_SMBTEMP);
+ free(pbuf, M_SMBTEMP);
+ smb_rq_done(rqp);
+ return error;
+}
+
+int
+smb_smb_ssnclose(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ int error;
+
+ if (vcp->vc_smbuid == SMB_UID_UNKNOWN)
+ return 0;
+
+ if (smb_smb_nomux(vcp, scred, __FUNCTION__) != 0)
+ return EINVAL;
+
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_LOGOFF_ANDX, scred, &rqp);
+ if (error)
+ return error;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, 0xff);
+ mb_put_uint8(mbp, 0);
+ mb_put_uint16le(mbp, 0);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ smb_rq_done(rqp);
+ return error;
+}
+
+static char smb_any_share[] = "?????";
+
+static char *
+smb_share_typename(int stype)
+{
+ char *pp;
+
+ switch (stype) {
+ case SMB_ST_DISK:
+ pp = "A:";
+ break;
+ case SMB_ST_PRINTER:
+ pp = smb_any_share; /* can't use LPT: here... */
+ break;
+ case SMB_ST_PIPE:
+ pp = "IPC";
+ break;
+ case SMB_ST_COMM:
+ pp = "COMM";
+ break;
+ case SMB_ST_ANY:
+ default:
+ pp = smb_any_share;
+ break;
+ }
+ return pp;
+}
+
+int
+smb_smb_treeconnect(struct smb_share *ssp, struct smb_cred *scred)
+{
+ struct smb_vc *vcp;
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ char *pp, *pbuf, *encpass;
+ int error, plen, caseopt;
+
+ ssp->ss_tid = SMB_TID_UNKNOWN;
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_CONNECT_ANDX, scred, &rqp);
+ if (error)
+ return error;
+ vcp = rqp->sr_vc;
+ caseopt = SMB_CS_NONE;
+ if (vcp->vc_sopt.sv_sm & SMB_SM_USER) {
+ plen = 1;
+ pp = "";
+ pbuf = NULL;
+ encpass = NULL;
+ } else {
+ pbuf = malloc(SMB_MAXPASSWORDLEN + 1, M_SMBTEMP, M_WAITOK);
+ encpass = malloc(24, M_SMBTEMP, M_WAITOK);
+ iconv_convstr(vcp->vc_toupper, pbuf, smb_share_getpass(ssp));
+ iconv_convstr(vcp->vc_toserver, pbuf, pbuf);
+ if (vcp->vc_sopt.sv_sm & SMB_SM_ENCRYPT) {
+ plen = 24;
+ smb_encrypt(pbuf, vcp->vc_ch, encpass);
+ pp = encpass;
+ } else {
+ plen = strlen(pbuf) + 1;
+ pp = pbuf;
+ }
+ }
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint8(mbp, 0xff);
+ mb_put_uint8(mbp, 0);
+ mb_put_uint16le(mbp, 0);
+ mb_put_uint16le(mbp, 0); /* Flags */
+ mb_put_uint16le(mbp, plen);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_mem(mbp, pp, plen, MB_MSYSTEM);
+ smb_put_dmem(mbp, vcp, "\\\\", 2, caseopt);
+ pp = vcp->vc_srvname;
+ smb_put_dmem(mbp, vcp, pp, strlen(pp), caseopt);
+ smb_put_dmem(mbp, vcp, "\\", 1, caseopt);
+ pp = ssp->ss_name;
+ smb_put_dstring(mbp, vcp, pp, caseopt);
+ pp = smb_share_typename(ssp->ss_type);
+ smb_put_dstring(mbp, vcp, pp, caseopt);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ if (error)
+ goto bad;
+ ssp->ss_tid = rqp->sr_rptid;
+ ssp->ss_vcgenid = vcp->vc_genid;
+ ssp->ss_flags |= SMBS_CONNECTED;
+bad:
+ if (encpass)
+ free(encpass, M_SMBTEMP);
+ if (pbuf)
+ free(pbuf, M_SMBTEMP);
+ smb_rq_done(rqp);
+ return error;
+}
+
+int
+smb_smb_treedisconnect(struct smb_share *ssp, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ int error;
+
+ if (ssp->ss_tid == SMB_TID_UNKNOWN)
+ return 0;
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_TREE_DISCONNECT, scred, &rqp);
+ if (error)
+ return error;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ smb_rq_done(rqp);
+ ssp->ss_tid = SMB_TID_UNKNOWN;
+ return error;
+}
+
+static __inline int
+smb_smb_read(struct smb_share *ssp, u_int16_t fid,
+ int *len, int *rresid, struct uio *uio, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int16_t resid, bc;
+ u_int8_t wc;
+ int error, rlen, blksz;
+
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_READ, scred, &rqp);
+ if (error)
+ return error;
+
+ blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
+ rlen = *len = min(blksz, *len);
+
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
+ mb_put_uint16le(mbp, rlen);
+ mb_put_uint32le(mbp, uio->uio_offset);
+ mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ smb_rq_bend(rqp);
+ do {
+ error = smb_rq_simple(rqp);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint8(mdp, &wc);
+ if (wc != 5) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint16le(mdp, &resid);
+ md_get_mem(mdp, NULL, 4 * 2, MB_MSYSTEM);
+ md_get_uint16le(mdp, &bc);
+ md_get_uint8(mdp, NULL); /* ignore buffer type */
+ md_get_uint16le(mdp, &resid);
+ if (resid == 0) {
+ *rresid = resid;
+ break;
+ }
+ error = md_get_uio(mdp, uio, resid);
+ if (error)
+ break;
+ *rresid = resid;
+ } while(0);
+ smb_rq_done(rqp);
+ return error;
+}
+
+int
+smb_read(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
+ struct smb_cred *scred)
+{
+ int tsize, len, resid;
+ int error = 0;
+
+ tsize = uio->uio_resid;
+ while (tsize > 0) {
+ len = tsize;
+ error = smb_smb_read(ssp, fid, &len, &resid, uio, scred);
+ if (error)
+ break;
+ tsize -= resid;
+ if (resid < len)
+ break;
+ }
+ return error;
+}
+
+static __inline int
+smb_smb_write(struct smb_share *ssp, u_int16_t fid, int *len, int *rresid,
+ struct uio *uio, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int16_t resid;
+ u_int8_t wc;
+ int error, blksz;
+
+ blksz = SSTOVC(ssp)->vc_txmax - SMB_HDRLEN - 16;
+ if (blksz > 0xffff)
+ blksz = 0xffff;
+
+ resid = *len = min(blksz, *len);
+
+ error = smb_rq_alloc(SSTOCP(ssp), SMB_COM_WRITE, scred, &rqp);
+ if (error)
+ return error;
+ smb_rq_getrequest(rqp, &mbp);
+ smb_rq_wstart(rqp);
+ mb_put_mem(mbp, (caddr_t)&fid, sizeof(fid), MB_MSYSTEM);
+ mb_put_uint16le(mbp, resid);
+ mb_put_uint32le(mbp, uio->uio_offset);
+ mb_put_uint16le(mbp, min(uio->uio_resid, 0xffff));
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint8(mbp, SMB_DT_DATA);
+ mb_put_uint16le(mbp, resid);
+ do {
+ error = mb_put_uio(mbp, uio, resid);
+ if (error)
+ break;
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ if (error)
+ break;
+ smb_rq_getreply(rqp, &mdp);
+ md_get_uint8(mdp, &wc);
+ if (wc != 1) {
+ error = EBADRPC;
+ break;
+ }
+ md_get_uint16le(mdp, &resid);
+ *rresid = resid;
+ } while(0);
+ smb_rq_done(rqp);
+ return error;
+}
+
+int
+smb_write(struct smb_share *ssp, u_int16_t fid, struct uio *uio,
+ struct smb_cred *scred)
+{
+ int error = 0, len, tsize, resid;
+ struct uio olduio;
+
+ /*
+ * review: manage iov more precisely
+ */
+ if (uio->uio_iovcnt != 1) {
+ SMBERROR("can't handle iovcnt > 1\n");
+ return EIO;
+ }
+ tsize = uio->uio_resid;
+ olduio = *uio;
+ while (tsize > 0) {
+ len = tsize;
+ error = smb_smb_write(ssp, fid, &len, &resid, uio, scred);
+ if (error)
+ break;
+ if (resid < len) {
+ error = EIO;
+ break;
+ }
+ tsize -= resid;
+ }
+ if (error) {
+ *uio = olduio;
+ }
+ return error;
+}
+
+int
+smb_smb_echo(struct smb_vc *vcp, struct smb_cred *scred)
+{
+ struct smb_rq *rqp;
+ struct mbchain *mbp;
+ int error;
+
+ error = smb_rq_alloc(VCTOCP(vcp), SMB_COM_ECHO, scred, &rqp);
+ if (error)
+ return error;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ mb_put_uint16le(mbp, 1);
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ mb_put_uint32le(mbp, 0);
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ SMBSDEBUG("%d\n", error);
+ smb_rq_done(rqp);
+ return error;
+}
diff --git a/sys/netsmb/smb_subr.c b/sys/netsmb/smb_subr.c
new file mode 100644
index 0000000..8346629
--- /dev/null
+++ b/sys/netsmb/smb_subr.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/lock.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/signalvar.h>
+#include <sys/mbuf.h>
+
+#include <sys/iconv.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_subr.h>
+
+MALLOC_DEFINE(M_SMBDATA, "SMBDATA", "Misc netsmb data");
+MALLOC_DEFINE(M_SMBSTR, "SMBSTR", "netsmb string data");
+MALLOC_DEFINE(M_SMBTEMP, "SMBTEMP", "Temp netsmb data");
+
+smb_unichar smb_unieol = 0;
+
+void
+smb_makescred(struct smb_cred *scred, struct proc *p, struct ucred *cred)
+{
+ if (p) {
+ scred->scr_p = p;
+ scred->scr_cred = cred ? cred : p->p_ucred;
+ } else {
+ scred->scr_p = NULL;
+ scred->scr_cred = cred ? cred : NULL;
+ }
+}
+
+int
+smb_proc_intr(struct proc *p)
+{
+#if __FreeBSD_version < 400009
+
+ if (p && p->p_siglist &&
+ (((p->p_siglist & ~p->p_sigmask) & ~p->p_sigignore) & SMB_SIGMASK))
+ return EINTR;
+ return 0;
+#else
+ sigset_t tmpset;
+
+ if (p == NULL)
+ return 0;
+ tmpset = p->p_siglist;
+ SIGSETNAND(tmpset, p->p_sigmask);
+ SIGSETNAND(tmpset, p->p_sigignore);
+ if (SIGNOTEMPTY(p->p_siglist) && SMB_SIGMASK(tmpset))
+ return EINTR;
+ return 0;
+#endif
+}
+
+char *
+smb_strdup(const char *s)
+{
+ char *p;
+ int len;
+
+ len = s ? strlen(s) + 1 : 1;
+ p = malloc(len, M_SMBSTR, M_WAITOK);
+ if (s)
+ bcopy(s, p, len);
+ else
+ *p = 0;
+ return p;
+}
+
+/*
+ * duplicate string from a user space.
+ */
+char *
+smb_strdupin(char *s, int maxlen)
+{
+ char *p, bt;
+ int len = 0;
+
+ for (p = s; ;p++) {
+ if (copyin(p, &bt, 1))
+ return NULL;
+ len++;
+ if (maxlen && len > maxlen)
+ return NULL;
+ if (bt == 0)
+ break;
+ }
+ p = malloc(len, M_SMBSTR, M_WAITOK);
+ copyin(s, p, len);
+ return p;
+}
+
+/*
+ * duplicate memory block from a user space.
+ */
+void *
+smb_memdupin(void *umem, int len)
+{
+ char *p;
+
+ if (len > 8 * 1024)
+ return NULL;
+ p = malloc(len, M_SMBSTR, M_WAITOK);
+ if (copyin(umem, p, len) == 0)
+ return p;
+ free(p, M_SMBSTR);
+ return NULL;
+}
+
+/*
+ * duplicate memory block in the kernel space.
+ */
+void *
+smb_memdup(const void *umem, int len)
+{
+ char *p;
+
+ if (len > 8 * 1024)
+ return NULL;
+ p = malloc(len, M_SMBSTR, M_WAITOK);
+ if (p == NULL)
+ return NULL;
+ bcopy(umem, p, len);
+ return p;
+}
+
+void
+smb_strfree(char *s)
+{
+ free(s, M_SMBSTR);
+}
+
+void
+smb_memfree(void *s)
+{
+ free(s, M_SMBSTR);
+}
+
+void *
+smb_zmalloc(unsigned long size, struct malloc_type *type, int flags)
+{
+
+ return malloc(size, type, flags | M_ZERO);
+}
+
+void
+smb_strtouni(u_int16_t *dst, const char *src)
+{
+ while (*src) {
+ *dst++ = htoles(*src++);
+ }
+ *dst = 0;
+}
+
+#ifdef SMB_SOCKETDATA_DEBUG
+void
+m_dumpm(struct mbuf *m) {
+ char *p;
+ int len;
+ printf("d=");
+ while(m) {
+ p=mtod(m,char *);
+ len=m->m_len;
+ printf("(%d)",len);
+ while(len--){
+ printf("%02x ",((int)*(p++)) & 0xff);
+ }
+ m=m->m_next;
+ };
+ printf("\n");
+}
+#endif
+
+int
+smb_maperror(int eclass, int eno)
+{
+ if (eclass == 0 && eno == 0)
+ return 0;
+ switch (eclass) {
+ case ERRDOS:
+ switch (eno) {
+ case ERRbadfunc:
+ case ERRbadmcb:
+ case ERRbadenv:
+ case ERRbadformat:
+ case ERRrmuns:
+ return EINVAL;
+ case ERRbadfile:
+ case ERRbadpath:
+ case ERRremcd:
+ case 66: /* nt returns it when share not available */
+ return ENOENT;
+ case ERRnofids:
+ return EMFILE;
+ case ERRnoaccess:
+ case ERRbadshare:
+ return EACCES;
+ case ERRbadfid:
+ return EBADF;
+ case ERRnomem:
+ return ENOMEM; /* actually remote no mem... */
+ case ERRbadmem:
+ return EFAULT;
+ case ERRbadaccess:
+ return EACCES;
+ case ERRbaddata:
+ return E2BIG;
+ case ERRbaddrive:
+ case ERRnotready: /* nt */
+ return ENXIO;
+ case ERRdiffdevice:
+ return EXDEV;
+ case ERRnofiles:
+ return 0; /* eeof ? */
+ return ETXTBSY;
+ case ERRlock:
+ return EDEADLK;
+ case ERRfilexists:
+ return EEXIST;
+ case 123: /* dunno what is it, but samba maps as noent */
+ return ENOENT;
+ case 145: /* samba */
+ return ENOTEMPTY;
+ case 183:
+ return EEXIST;
+ }
+ break;
+ case ERRSRV:
+ switch (eno) {
+ case ERRerror:
+ return EINVAL;
+ case ERRbadpw:
+ return EAUTH;
+ case ERRaccess:
+ return EACCES;
+ case ERRinvnid:
+ return ENETRESET;
+ case ERRinvnetname:
+ SMBERROR("NetBIOS name is invalid\n");
+ return EAUTH;
+ case 3: /* reserved and returned */
+ return EIO;
+ case 2239: /* NT: account exists but disabled */
+ return EPERM;
+ }
+ break;
+ case ERRHRD:
+ switch (eno) {
+ case ERRnowrite:
+ return EROFS;
+ case ERRbadunit:
+ return ENODEV;
+ case ERRnotready:
+ case ERRbadcmd:
+ case ERRdata:
+ return EIO;
+ case ERRbadreq:
+ return EBADRPC;
+ case ERRbadshare:
+ return ETXTBSY;
+ case ERRlock:
+ return EDEADLK;
+ }
+ break;
+ }
+ SMBERROR("Unmapped error %d:%d\n", eclass, eno);
+ return EBADRPC;
+}
+
+static int
+smb_copy_iconv(struct mbchain *mbp, c_caddr_t src, caddr_t dst, int len)
+{
+ int outlen = len;
+
+ return iconv_conv((struct iconv_drv*)mbp->mb_udata, &src, &len, &dst, &outlen);
+}
+
+int
+smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp, const char *src,
+ int size, int caseopt)
+{
+ struct iconv_drv *dp = vcp->vc_toserver;
+
+ if (size == 0)
+ return 0;
+ if (dp == NULL) {
+ return mb_put_mem(mbp, src, size, MB_MSYSTEM);
+ }
+ mbp->mb_copy = smb_copy_iconv;
+ mbp->mb_udata = dp;
+ return mb_put_mem(mbp, src, size, MB_MCUSTOM);
+}
+
+int
+smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp, const char *src,
+ int caseopt)
+{
+ int error;
+
+ error = smb_put_dmem(mbp, vcp, src, strlen(src), caseopt);
+ if (error)
+ return error;
+ return mb_put_uint8(mbp, 0);
+}
+
+int
+smb_put_asunistring(struct smb_rq *rqp, const char *src)
+{
+ struct mbchain *mbp = &rqp->sr_rq;
+ struct iconv_drv *dp = rqp->sr_vc->vc_toserver;
+ u_char c;
+ int error;
+
+ while (*src) {
+ iconv_convmem(dp, &c, src++, 1);
+ error = mb_put_uint16le(mbp, c);
+ if (error)
+ return error;
+ }
+ return mb_put_uint16le(mbp, 0);
+}
diff --git a/sys/netsmb/smb_subr.h b/sys/netsmb/smb_subr.h
new file mode 100644
index 0000000..16ce212
--- /dev/null
+++ b/sys/netsmb/smb_subr.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NETSMB_SMB_SUBR_H_
+#define _NETSMB_SMB_SUBR_H_
+
+#ifndef _KERNEL
+#error "This file shouldn't be included from userland programs"
+#endif
+
+#ifdef MALLOC_DECLARE
+MALLOC_DECLARE(M_SMBTEMP);
+#endif
+
+#if __FreeBSD_version > 500000
+#define FB_CURRENT
+#else
+# if __FreeBSD_version > 400000
+# define FB_RELENG4
+# else
+# error "Unsupported version of FreeBSD"
+# endif
+#endif
+
+#define SMBERROR(format, args...) printf("%s: "format, __FUNCTION__ ,## args)
+#define SMBPANIC(format, args...) printf("%s: "format, __FUNCTION__ ,## args)
+
+#ifdef SMB_SOCKET_DEBUG
+#define SMBSDEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args)
+#else
+#define SMBSDEBUG(format, args...)
+#endif
+
+#ifdef SMB_IOD_DEBUG
+#define SMBIODEBUG(format, args...) printf("%s: "format, __FUNCTION__ ,## args)
+#else
+#define SMBIODEBUG(format, args...)
+#endif
+
+#ifdef SMB_SOCKETDATA_DEBUG
+void m_dumpm(struct mbuf *m);
+#else
+#define m_dumpm(m)
+#endif
+
+#if __FreeBSD_version > 400009
+#define SMB_SIGMASK(set) \
+ (SIGISMEMBER(set, SIGINT) || SIGISMEMBER(set, SIGTERM) || \
+ SIGISMEMBER(set, SIGHUP) || SIGISMEMBER(set, SIGKILL) || \
+ SIGISMEMBER(set, SIGQUIT))
+
+#define smb_suser(cred) suser_xxx(cred, NULL, 0)
+#else
+#define SMB_SIGMASK (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGKILL)| \
+ sigmask(SIGHUP)|sigmask(SIGQUIT))
+
+#define smb_suser(cred) suser((cred), NULL)
+#endif
+
+/*
+ * Compatibility wrappers for simple locks
+ */
+#if __FreeBSD_version < 500000
+
+#include <sys/lock.h>
+
+#define lockdestroy(lock)
+#define smb_slock simplelock
+#define smb_sl_init(mtx, desc) simple_lock_init(mtx)
+#define smb_sl_destroy(mtx)
+#define smb_sl_lock(mtx) simple_lock(mtx)
+#define smb_sl_unlock(mtx) simple_unlock(mtx)
+/*
+#define mtx lock
+#define mtx_init(mtx, desc, flags) lockinit(mtx, PWAIT, desc, 0, 0)
+#define mtx_lock(mtx) lockmgr(mtx, LK_EXCLUSIVE, NULL, curproc)
+#define mtx_unlock(mtx) lockmgr(mtx, LK_RELEASE, NULL, curproc)
+#define mtx_destroy(mtx)
+*/
+#else
+
+#include <sys/mutex.h>
+
+#define smb_slock mtx
+#define smb_sl_init(mtx, desc) mtx_init(mtx, desc, MTX_DEF)
+#define smb_sl_destroy(mtx) mtx_destroy(mtx)
+#define smb_sl_lock(mtx) mtx_lock(mtx)
+#define smb_sl_unlock(mtx) mtx_unlock(mtx)
+
+#endif
+
+#define SMB_STRFREE(p) do { if (p) smb_strfree(p); } while(0)
+
+/*
+ * The simple try/catch/finally interface.
+ * With GCC it is possible to allow more than one try/finally block per
+ * function, but we'll avoid it to maintain portability.
+ */
+#define itry { \
+ __label__ _finlab, _catchlab; \
+ int _tval; \
+
+#define icatch(var) \
+ goto _finlab; \
+ (void)&&_catchlab; \
+ _catchlab: \
+ var = _tval;
+
+#define ifinally (void)&&_finlab; \
+ _finlab:
+#define iendtry }
+
+#define inocatch \
+ goto _finlab; \
+ (void)&&_catchlab; \
+ _catchlab: \
+
+#define ithrow(t) do { \
+ if ((_tval = (int)(t)) != 0) \
+ goto _catchlab; \
+ } while (0)
+
+#define ierror(t,e) do { \
+ if (t) { \
+ _tval = e; \
+ goto _catchlab; \
+ } \
+ } while (0)
+
+typedef u_int16_t smb_unichar;
+typedef smb_unichar *smb_uniptr;
+
+/*
+ * Crediantials of user/process being processing in the connection procedures
+ */
+struct smb_cred {
+ struct proc * scr_p;
+ struct ucred * scr_cred;
+};
+
+extern smb_unichar smb_unieol;
+
+struct mbchain;
+struct smb_vc;
+struct smb_rq;
+
+void smb_makescred(struct smb_cred *scred, struct proc *p, struct ucred *cred);
+int smb_proc_intr(struct proc *);
+char *smb_strdup(const char *s);
+void *smb_memdup(const void *umem, int len);
+char *smb_strdupin(char *s, int maxlen);
+void *smb_memdupin(void *umem, int len);
+void smb_strtouni(u_int16_t *dst, const char *src);
+void smb_strfree(char *s);
+void smb_memfree(void *s);
+void *smb_zmalloc(unsigned long size, struct malloc_type *type, int flags);
+
+int smb_encrypt(const u_char *apwd, u_char *C8, u_char *RN);
+int smb_ntencrypt(const u_char *apwd, u_char *C8, u_char *RN);
+int smb_maperror(int eclass, int eno);
+int smb_put_dmem(struct mbchain *mbp, struct smb_vc *vcp,
+ const char *src, int len, int caseopt);
+int smb_put_dstring(struct mbchain *mbp, struct smb_vc *vcp,
+ const char *src, int caseopt);
+int smb_put_string(struct smb_rq *rqp, const char *src);
+int smb_put_asunistring(struct smb_rq *rqp, const char *src);
+
+#endif /* !_NETSMB_SMB_SUBR_H_ */
diff --git a/sys/netsmb/smb_tran.h b/sys/netsmb/smb_tran.h
new file mode 100644
index 0000000..7403d21
--- /dev/null
+++ b/sys/netsmb/smb_tran.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _NETSMB_SMB_TRAN_H_
+#define _NETSMB_SMB_TRAN_H_
+
+#include <sys/socket.h>
+
+/*
+ * Known transports
+ */
+#define SMBT_NBTCP 1
+
+/*
+ * Transport parameters
+ */
+#define SMBTP_SNDSZ 1 /* R - int */
+#define SMBTP_RCVSZ 2 /* R - int */
+#define SMBTP_TIMEOUT 3 /* RW - struct timespec */
+#define SMBTP_SELECTID 4 /* RW - (void *) */
+
+struct smb_tran_ops;
+
+struct smb_tran_desc {
+ sa_family_t tr_type;
+ int (*tr_create)(struct smb_vc *vcp, struct proc *p);
+ int (*tr_done)(struct smb_vc *vcp, struct proc *p);
+ int (*tr_bind)(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p);
+ int (*tr_connect)(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p);
+ int (*tr_disconnect)(struct smb_vc *vcp, struct proc *p);
+ int (*tr_send)(struct smb_vc *vcp, struct mbuf *m0, struct proc *p);
+ int (*tr_recv)(struct smb_vc *vcp, struct mbuf **mpp, struct proc *p);
+ void (*tr_timo)(struct smb_vc *vcp);
+ void (*tr_intr)(struct smb_vc *vcp);
+ int (*tr_getparam)(struct smb_vc *vcp, int param, void *data);
+ int (*tr_setparam)(struct smb_vc *vcp, int param, void *data);
+ int (*tr_fatal)(struct smb_vc *vcp, int error);
+#ifdef notyet
+ int (*tr_poll)(struct smb_vc *vcp, struct proc *p);
+ int (*tr_cmpaddr)(void *addr1, void *addr2);
+#endif
+ LIST_ENTRY(smb_tran_desc) tr_link;
+};
+
+#define SMB_TRAN_CREATE(vcp,p) (vcp)->vc_tdesc->tr_create(vcp,p)
+#define SMB_TRAN_DONE(vcp,p) (vcp)->vc_tdesc->tr_done(vcp,p)
+#define SMB_TRAN_BIND(vcp,sap,p) (vcp)->vc_tdesc->tr_bind(vcp,sap,p)
+#define SMB_TRAN_CONNECT(vcp,sap,p) (vcp)->vc_tdesc->tr_connect(vcp,sap,p)
+#define SMB_TRAN_DISCONNECT(vcp,p) (vcp)->vc_tdesc->tr_disconnect(vcp,p)
+#define SMB_TRAN_SEND(vcp,m0,p) (vcp)->vc_tdesc->tr_send(vcp,m0,p)
+#define SMB_TRAN_RECV(vcp,m,p) (vcp)->vc_tdesc->tr_recv(vcp,m,p)
+#define SMB_TRAN_TIMO(vcp) (vcp)->vc_tdesc->tr_timo(vcp)
+#define SMB_TRAN_INTR(vcp) (vcp)->vc_tdesc->tr_intr(vcp)
+#define SMB_TRAN_GETPARAM(vcp,par,data) (vcp)->vc_tdesc->tr_getparam(vcp, par, data)
+#define SMB_TRAN_SETPARAM(vcp,par,data) (vcp)->vc_tdesc->tr_setparam(vcp, par, data)
+#define SMB_TRAN_FATAL(vcp, error) (vcp)->vc_tdesc->tr_fatal(vcp, error)
+
+#endif /* _NETSMB_SMB_TRAN_H_ */
diff --git a/sys/netsmb/smb_trantcp.c b/sys/netsmb/smb_trantcp.c
new file mode 100644
index 0000000..c9d0622
--- /dev/null
+++ b/sys/netsmb/smb_trantcp.c
@@ -0,0 +1,672 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/proc.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/poll.h>
+#include <sys/uio.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+#include <sys/mchain.h>
+
+#include <netsmb/netbios.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_tran.h>
+#include <netsmb/smb_trantcp.h>
+#include <netsmb/smb_subr.h>
+
+#define M_NBDATA M_PCB
+
+static int smb_tcpsndbuf = 10 * 1024;
+static int smb_tcprcvbuf = 10 * 1024;
+
+SYSCTL_DECL(_net_smb);
+SYSCTL_INT(_net_smb, OID_AUTO, tcpsndbuf, CTLFLAG_RW, &smb_tcpsndbuf, 0, "");
+SYSCTL_INT(_net_smb, OID_AUTO, tcprcvbuf, CTLFLAG_RW, &smb_tcprcvbuf, 0, "");
+
+#define nb_sosend(so,m,flags,p) (so)->so_proto->pr_usrreqs->pru_sosend( \
+ so, NULL, 0, m, 0, flags, p)
+
+static int nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp,
+ u_int8_t *rpcodep, struct proc *p);
+static int smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p);
+
+static int
+nb_setsockopt_int(struct socket *so, int level, int name, int val)
+{
+ struct sockopt sopt;
+
+ bzero(&sopt, sizeof(sopt));
+ sopt.sopt_level = level;
+ sopt.sopt_name = name;
+ sopt.sopt_val = &val;
+ sopt.sopt_valsize = sizeof(val);
+ return sosetopt(so, &sopt);
+}
+
+static __inline int
+nb_poll(struct nbpcb *nbp, int events, struct proc *p)
+{
+ return nbp->nbp_tso->so_proto->pr_usrreqs->pru_sopoll(nbp->nbp_tso,
+ events, NULL, p);
+}
+
+static int
+nbssn_rselect(struct nbpcb *nbp, struct timeval *tv, int events, struct proc *p)
+{
+ struct timeval atv, rtv, ttv;
+ int s, timo, error;
+
+ if (tv) {
+ atv = *tv;
+ if (itimerfix(&atv)) {
+ error = EINVAL;
+ goto done;
+ }
+ getmicrouptime(&rtv);
+ timevaladd(&atv, &rtv);
+ }
+ timo = 0;
+retry:
+ p->p_flag |= P_SELECT;
+ error = nb_poll(nbp, events, p);
+ if (error) {
+ error = 0;
+ goto done;
+ }
+ if (tv) {
+ getmicrouptime(&rtv);
+ if (timevalcmp(&rtv, &atv, >=))
+ goto done;
+ ttv = atv;
+ timevalsub(&ttv, &rtv);
+ timo = tvtohz(&ttv);
+ }
+ s = splhigh();
+ if ((p->p_flag & P_SELECT) == 0) {
+ splx(s);
+ goto retry;
+ }
+ p->p_flag &= ~P_SELECT;
+ error = tsleep((caddr_t)&selwait, PSOCK, "nbsel", timo);
+ splx(s);
+done:
+ p->p_flag &= ~P_SELECT;
+ if (error == ERESTART)
+ return 0;
+ return error;
+}
+
+static int
+nb_intr(struct nbpcb *nbp, struct proc *p)
+{
+ return 0;
+}
+
+static void
+nb_upcall(struct socket *so, void *arg, int waitflag)
+{
+ struct nbpcb *nbp = arg;
+
+ if (arg == NULL || nbp->nbp_selectid == NULL)
+ return;
+ wakeup(nbp->nbp_selectid);
+}
+
+static int
+nb_sethdr(struct mbuf *m, u_int8_t type, u_int32_t len)
+{
+ u_int32_t *p = mtod(m, u_int32_t *);
+
+ *p = htonl((len & 0x1FFFF) | (type << 24));
+ return 0;
+}
+
+static int
+nb_put_name(struct mbchain *mbp, struct sockaddr_nb *snb)
+{
+ int error;
+ u_char seglen, *cp;
+
+ cp = snb->snb_name;
+ if (*cp == 0)
+ return EINVAL;
+ NBDEBUG("[%s]\n", cp);
+ for (;;) {
+ seglen = (*cp) + 1;
+ error = mb_put_mem(mbp, cp, seglen, MB_MSYSTEM);
+ if (error)
+ return error;
+ if (seglen == 1)
+ break;
+ cp += seglen;
+ }
+ return 0;
+}
+
+static int
+nb_connect_in(struct nbpcb *nbp, struct sockaddr_in *to, struct proc *p)
+{
+ struct socket *so;
+ int error, s;
+
+ error = socreate(AF_INET, &so, SOCK_STREAM, IPPROTO_TCP, p);
+ if (error)
+ return error;
+ nbp->nbp_tso = so;
+ so->so_upcallarg = (caddr_t)nbp;
+ so->so_upcall = nb_upcall;
+ so->so_rcv.sb_flags |= SB_UPCALL;
+ so->so_rcv.sb_timeo = (5 * hz);
+ so->so_snd.sb_timeo = (5 * hz);
+ error = soreserve(so, nbp->nbp_sndbuf, nbp->nbp_rcvbuf);
+ if (error)
+ goto bad;
+ nb_setsockopt_int(so, SOL_SOCKET, SO_KEEPALIVE, 1);
+ nb_setsockopt_int(so, IPPROTO_TCP, TCP_NODELAY, 1);
+ so->so_rcv.sb_flags &= ~SB_NOINTR;
+ so->so_snd.sb_flags &= ~SB_NOINTR;
+ error = soconnect(so, (struct sockaddr*)to, p);
+ if (error)
+ goto bad;
+ s = splnet();
+ while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) {
+ tsleep(&so->so_timeo, PSOCK, "nbcon", 2 * hz);
+ if ((so->so_state & SS_ISCONNECTING) && so->so_error == 0 &&
+ (error = nb_intr(nbp, p)) != 0) {
+ so->so_state &= ~SS_ISCONNECTING;
+ splx(s);
+ goto bad;
+ }
+ }
+ if (so->so_error) {
+ error = so->so_error;
+ so->so_error = 0;
+ splx(s);
+ goto bad;
+ }
+ splx(s);
+ return 0;
+bad:
+ smb_nbst_disconnect(nbp->nbp_vc, p);
+ return error;
+}
+
+static int
+nbssn_rq_request(struct nbpcb *nbp, struct proc *p)
+{
+ struct mbchain mb, *mbp = &mb;
+ struct mdchain md, *mdp = &md;
+ struct mbuf *m0;
+ struct timeval tv;
+ struct sockaddr_in sin;
+ u_short port;
+ u_int8_t rpcode;
+ int error, rplen;
+
+ error = mb_init(mbp);
+ if (error)
+ return error;
+ mb_put_uint32le(mbp, 0);
+ nb_put_name(mbp, nbp->nbp_paddr);
+ nb_put_name(mbp, nbp->nbp_laddr);
+ nb_sethdr(mbp->mb_top, NB_SSN_REQUEST, mb_fixhdr(mbp) - 4);
+ error = nb_sosend(nbp->nbp_tso, mbp->mb_top, 0, p);
+ if (!error) {
+ nbp->nbp_state = NBST_RQSENT;
+ }
+ mb_detach(mbp);
+ mb_done(mbp);
+ if (error)
+ return error;
+ TIMESPEC_TO_TIMEVAL(&tv, &nbp->nbp_timo);
+ error = nbssn_rselect(nbp, &tv, POLLIN, p);
+ if (error == EWOULDBLOCK) { /* Timeout */
+ NBDEBUG("initial request timeout\n");
+ return ETIMEDOUT;
+ }
+ if (error) /* restart or interrupt */
+ return error;
+ error = nbssn_recv(nbp, &m0, &rplen, &rpcode, p);
+ if (error) {
+ NBDEBUG("recv() error %d\n", error);
+ return error;
+ }
+ /*
+ * Process NETBIOS reply
+ */
+ if (m0)
+ md_initm(mdp, m0);
+ error = 0;
+ do {
+ if (rpcode == NB_SSN_POSRESP) {
+ nbp->nbp_state = NBST_SESSION;
+ nbp->nbp_flags |= NBF_CONNECTED;
+ break;
+ }
+ if (rpcode != NB_SSN_RTGRESP) {
+ error = ECONNABORTED;
+ break;
+ }
+ if (rplen != 6) {
+ error = ECONNABORTED;
+ break;
+ }
+ md_get_mem(mdp, (caddr_t)&sin.sin_addr, 4, MB_MSYSTEM);
+ md_get_uint16(mdp, &port);
+ sin.sin_port = port;
+ nbp->nbp_state = NBST_RETARGET;
+ smb_nbst_disconnect(nbp->nbp_vc, p);
+ error = nb_connect_in(nbp, &sin, p);
+ if (!error)
+ error = nbssn_rq_request(nbp, p);
+ if (error) {
+ smb_nbst_disconnect(nbp->nbp_vc, p);
+ break;
+ }
+ } while(0);
+ if (m0)
+ md_done(mdp);
+ return error;
+}
+
+static int
+nbssn_recvhdr(struct nbpcb *nbp, int *lenp,
+ u_int8_t *rpcodep, int flags, struct proc *p)
+{
+ struct socket *so = nbp->nbp_tso;
+ struct uio auio;
+ struct iovec aio;
+ u_int32_t len;
+ int error;
+
+ aio.iov_base = (caddr_t)&len;
+ aio.iov_len = sizeof(len);
+ auio.uio_iov = &aio;
+ auio.uio_iovcnt = 1;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_READ;
+ auio.uio_offset = 0;
+ auio.uio_resid = sizeof(len);
+ auio.uio_procp = p;
+ error = so->so_proto->pr_usrreqs->pru_soreceive
+ (so, (struct sockaddr **)NULL, &auio,
+ (struct mbuf **)NULL, (struct mbuf **)NULL, &flags);
+ if (error)
+ return error;
+ if (auio.uio_resid > 0) {
+ SMBSDEBUG("short reply\n");
+ return EPIPE;
+ }
+ len = ntohl(len);
+ *rpcodep = (len >> 24) & 0xFF;
+ len &= 0x1ffff;
+ if (len > SMB_MAXPKTLEN) {
+ SMBERROR("packet too long (%d)\n", len);
+ return EFBIG;
+ }
+ *lenp = len;
+ return 0;
+}
+
+static int
+nbssn_recv(struct nbpcb *nbp, struct mbuf **mpp, int *lenp,
+ u_int8_t *rpcodep, struct proc *p)
+{
+ struct socket *so = nbp->nbp_tso;
+ struct uio auio;
+ struct mbuf *m;
+ u_int8_t rpcode;
+ int len;
+ int error, rcvflg;
+
+ if (so == NULL)
+ return ENOTCONN;
+
+ if (mpp)
+ *mpp = NULL;
+ for(;;) {
+ m = NULL;
+ error = nbssn_recvhdr(nbp, &len, &rpcode, MSG_DONTWAIT, p);
+ if (so->so_state &
+ (SS_ISDISCONNECTING | SS_ISDISCONNECTED | SS_CANTRCVMORE)) {
+ nbp->nbp_state = NBST_CLOSED;
+ NBDEBUG("session closed by peer\n");
+ return ECONNRESET;
+ }
+ if (error)
+ return error;
+ if (len == 0 && nbp->nbp_state != NBST_SESSION)
+ break;
+ if (rpcode == NB_SSN_KEEPALIVE)
+ continue;
+ bzero(&auio, sizeof(auio));
+ auio.uio_resid = len;
+ auio.uio_procp = p;
+ do {
+ rcvflg = MSG_WAITALL;
+ error = so->so_proto->pr_usrreqs->pru_soreceive
+ (so, (struct sockaddr **)NULL,
+ &auio, &m, (struct mbuf **)NULL, &rcvflg);
+ } while (error == EWOULDBLOCK || error == EINTR ||
+ error == ERESTART);
+ if (error)
+ break;
+ if (auio.uio_resid > 0) {
+ SMBERROR("packet is shorter than expected\n");
+ error = EPIPE;
+ break;
+ }
+ if (nbp->nbp_state == NBST_SESSION &&
+ rpcode == NB_SSN_MESSAGE)
+ break;
+ NBDEBUG("non-session packet %x\n", rpcode);
+ if (m)
+ m_freem(m);
+ }
+ if (error) {
+ if (m)
+ m_freem(m);
+ return error;
+ }
+ if (mpp)
+ *mpp = m;
+ else
+ m_freem(m);
+ *lenp = len;
+ *rpcodep = rpcode;
+ return 0;
+}
+
+/*
+ * SMB transport interface
+ */
+static int
+smb_nbst_create(struct smb_vc *vcp, struct proc *p)
+{
+ struct nbpcb *nbp;
+
+ MALLOC(nbp, struct nbpcb *, sizeof *nbp, M_NBDATA, M_WAITOK);
+ bzero(nbp, sizeof *nbp);
+ nbp->nbp_timo.tv_sec = 15; /* XXX: sysctl ? */
+ nbp->nbp_state = NBST_CLOSED;
+ nbp->nbp_vc = vcp;
+ nbp->nbp_sndbuf = smb_tcpsndbuf;
+ nbp->nbp_rcvbuf = smb_tcprcvbuf;
+ vcp->vc_tdata = nbp;
+ return 0;
+}
+
+static int
+smb_nbst_done(struct smb_vc *vcp, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+
+ if (nbp == NULL)
+ return ENOTCONN;
+ smb_nbst_disconnect(vcp, p);
+ if (nbp->nbp_laddr)
+ free(nbp->nbp_laddr, M_SONAME);
+ if (nbp->nbp_paddr)
+ free(nbp->nbp_paddr, M_SONAME);
+ free(nbp, M_NBDATA);
+ return 0;
+}
+
+static int
+smb_nbst_bind(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ struct sockaddr_nb *snb;
+ int error, slen;
+
+ NBDEBUG("\n");
+ error = EINVAL;
+ do {
+ if (nbp->nbp_flags & NBF_LOCADDR)
+ break;
+ /*
+ * It is possible to create NETBIOS name in the kernel,
+ * but nothing prevents us to do it in the user space.
+ */
+ if (sap == NULL)
+ break;
+ slen = sap->sa_len;
+ if (slen < NB_MINSALEN)
+ break;
+ snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1);
+ if (snb == NULL) {
+ error = ENOMEM;
+ break;
+ }
+ nbp->nbp_laddr = snb;
+ nbp->nbp_flags |= NBF_LOCADDR;
+ error = 0;
+ } while(0);
+ return error;
+}
+
+static int
+smb_nbst_connect(struct smb_vc *vcp, struct sockaddr *sap, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ struct sockaddr_in sin;
+ struct sockaddr_nb *snb;
+ struct timespec ts1, ts2;
+ int error, slen;
+
+ NBDEBUG("\n");
+ if (nbp->nbp_tso != NULL)
+ return EISCONN;
+ if (nbp->nbp_laddr == NULL)
+ return EINVAL;
+ slen = sap->sa_len;
+ if (slen < NB_MINSALEN)
+ return EINVAL;
+ if (nbp->nbp_paddr) {
+ free(nbp->nbp_paddr, M_SONAME);
+ nbp->nbp_paddr = NULL;
+ }
+ snb = (struct sockaddr_nb*)dup_sockaddr(sap, 1);
+ if (snb == NULL)
+ return ENOMEM;
+ nbp->nbp_paddr = snb;
+ sin = snb->snb_addrin;
+ getnanotime(&ts1);
+ error = nb_connect_in(nbp, &sin, p);
+ if (error)
+ return error;
+ getnanotime(&ts2);
+ timespecsub(&ts2, &ts1);
+ if (ts2.tv_sec == 0 && ts2.tv_sec == 0)
+ ts2.tv_sec = 1;
+ nbp->nbp_timo = ts2;
+ timespecadd(&nbp->nbp_timo, &ts2);
+ timespecadd(&nbp->nbp_timo, &ts2);
+ timespecadd(&nbp->nbp_timo, &ts2); /* * 4 */
+ error = nbssn_rq_request(nbp, p);
+ if (error)
+ smb_nbst_disconnect(vcp, p);
+ return error;
+}
+
+static int
+smb_nbst_disconnect(struct smb_vc *vcp, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ struct socket *so;
+
+ if (nbp == NULL || nbp->nbp_tso == NULL)
+ return ENOTCONN;
+ if ((so = nbp->nbp_tso) != NULL) {
+ nbp->nbp_flags &= ~NBF_CONNECTED;
+ nbp->nbp_tso = (struct socket *)NULL;
+ soshutdown(so, 2);
+ soclose(so);
+ }
+ if (nbp->nbp_state != NBST_RETARGET) {
+ nbp->nbp_state = NBST_CLOSED;
+ }
+ return 0;
+}
+
+static int
+smb_nbst_send(struct smb_vc *vcp, struct mbuf *m0, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ int error;
+
+ if (nbp->nbp_state != NBST_SESSION) {
+ error = ENOTCONN;
+ goto abort;
+ }
+ M_PREPEND(m0, 4, M_WAITOK);
+ if (m0 == NULL)
+ return ENOBUFS;
+ nb_sethdr(m0, NB_SSN_MESSAGE, m_fixhdr(m0) - 4);
+ error = nb_sosend(nbp->nbp_tso, m0, 0, p);
+ return error;
+abort:
+ if (m0)
+ m_freem(m0);
+ return error;
+}
+
+
+static int
+smb_nbst_recv(struct smb_vc *vcp, struct mbuf **mpp, struct proc *p)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+ u_int8_t rpcode;
+ int error, rplen;
+
+ nbp->nbp_flags |= NBF_RECVLOCK;
+ error = nbssn_recv(nbp, mpp, &rplen, &rpcode, p);
+ nbp->nbp_flags &= ~NBF_RECVLOCK;
+ return error;
+}
+
+static void
+smb_nbst_timo(struct smb_vc *vcp)
+{
+ return;
+}
+
+static void
+smb_nbst_intr(struct smb_vc *vcp)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+
+ if (nbp == NULL || nbp->nbp_tso == NULL)
+ return;
+ sorwakeup(nbp->nbp_tso);
+ sowwakeup(nbp->nbp_tso);
+}
+
+static int
+smb_nbst_getparam(struct smb_vc *vcp, int param, void *data)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+
+ switch (param) {
+ case SMBTP_SNDSZ:
+ *(int*)data = nbp->nbp_sndbuf;
+ break;
+ case SMBTP_RCVSZ:
+ *(int*)data = nbp->nbp_rcvbuf;
+ break;
+ case SMBTP_TIMEOUT:
+ *(struct timespec*)data = nbp->nbp_timo;
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+smb_nbst_setparam(struct smb_vc *vcp, int param, void *data)
+{
+ struct nbpcb *nbp = vcp->vc_tdata;
+
+ switch (param) {
+ case SMBTP_SELECTID:
+ nbp->nbp_selectid = data;
+ break;
+ default:
+ return EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Check for fatal errors
+ */
+static int
+smb_nbst_fatal(struct smb_vc *vcp, int error)
+{
+ switch (error) {
+ case ENOTCONN:
+ case ENETRESET:
+ case ECONNABORTED:
+ return 1;
+ }
+ return 0;
+}
+
+
+struct smb_tran_desc smb_tran_nbtcp_desc = {
+ SMBT_NBTCP,
+ smb_nbst_create, smb_nbst_done,
+ smb_nbst_bind, smb_nbst_connect, smb_nbst_disconnect,
+ smb_nbst_send, smb_nbst_recv,
+ smb_nbst_timo, smb_nbst_intr,
+ smb_nbst_getparam, smb_nbst_setparam,
+ smb_nbst_fatal
+};
+
diff --git a/sys/netsmb/smb_trantcp.h b/sys/netsmb/smb_trantcp.h
new file mode 100644
index 0000000..6c625fa
--- /dev/null
+++ b/sys/netsmb/smb_trantcp.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2000-2001, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NETSMB_SMB_TRANTCP_H_
+#define _NETSMB_SMB_TRANTCP_H_
+
+#ifdef _KERNEL
+
+#ifdef NB_DEBUG
+#define NBDEBUG(format, args...) printf("%s(%d): "format, \
+ __FUNCTION__ , __LINE__ ,## args)
+#else
+#define NBDEBUG(format, args...)
+#endif
+
+enum nbstate {
+ NBST_CLOSED,
+ NBST_RQSENT,
+ NBST_SESSION,
+ NBST_RETARGET,
+ NBST_REFUSED
+};
+
+
+/*
+ * socket specific data
+ */
+struct nbpcb {
+ struct smb_vc * nbp_vc;
+ struct socket * nbp_tso; /* transport socket */
+ struct sockaddr_nb *nbp_laddr; /* local address */
+ struct sockaddr_nb *nbp_paddr; /* peer address */
+
+ int nbp_flags;
+#define NBF_LOCADDR 0x0001 /* has local addr */
+#define NBF_CONNECTED 0x0002
+#define NBF_RECVLOCK 0x0004
+
+ enum nbstate nbp_state;
+ struct timespec nbp_timo;
+ int nbp_sndbuf;
+ int nbp_rcvbuf;
+ void * nbp_selectid;
+
+/* LIST_ENTRY(nbpcb) nbp_link;*/
+};
+
+/*
+ * Nominal space allocated per a NETBIOS socket.
+ */
+#define NB_SNDQ (10 * 1024)
+#define NB_RCVQ (20 * 1024)
+
+extern struct smb_tran_desc smb_tran_nbtcp_desc;
+
+#endif /* _KERNEL */
+
+#endif /* !_NETSMB_SMB_TRANTCP_H_ */
diff --git a/sys/netsmb/smb_usr.c b/sys/netsmb/smb_usr.c
new file mode 100644
index 0000000..2759173
--- /dev/null
+++ b/sys/netsmb/smb_usr.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2000-2001 Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Boris Popov.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/proc.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/sysctl.h>
+
+#include <sys/iconv.h>
+
+#include <netsmb/smb.h>
+#include <netsmb/smb_conn.h>
+#include <netsmb/smb_rq.h>
+#include <netsmb/smb_subr.h>
+#include <netsmb/smb_dev.h>
+
+/*
+ * helpers for nsmb device. Can be moved to the smb_dev.c file.
+ */
+static void smb_usr_vcspec_free(struct smb_vcspec *spec);
+
+static int
+smb_usr_vc2spec(struct smbioc_ossn *dp, struct smb_vcspec *spec)
+{
+ int flags = 0;
+
+ bzero(spec, sizeof(*spec));
+ if (dp->ioc_user[0] == 0)
+ return EINVAL;
+ if (dp->ioc_server == NULL)
+ return EINVAL;
+ if (dp->ioc_localcs[0] == 0) {
+ SMBERROR("no local charset ?\n");
+ return EINVAL;
+ }
+
+ spec->sap = smb_memdupin(dp->ioc_server, dp->ioc_svlen);
+ if (spec->sap == NULL)
+ return ENOMEM;
+ if (dp->ioc_local) {
+ spec->lap = smb_memdupin(dp->ioc_local, dp->ioc_lolen);
+ if (spec->lap == NULL) {
+ smb_usr_vcspec_free(spec);
+ return ENOMEM;
+ }
+ }
+ spec->srvname = dp->ioc_srvname;
+ spec->pass = dp->ioc_password;
+ spec->domain = dp->ioc_workgroup;
+ spec->username = dp->ioc_user;
+ spec->mode = dp->ioc_mode;
+ spec->rights = dp->ioc_rights;
+ spec->owner = dp->ioc_owner;
+ spec->group = dp->ioc_group;
+ spec->localcs = dp->ioc_localcs;
+ spec->servercs = dp->ioc_servercs;
+ if (dp->ioc_opt & SMBVOPT_PRIVATE)
+ flags |= SMBV_PRIVATE;
+ if (dp->ioc_opt & SMBVOPT_SINGLESHARE)
+ flags |= SMBV_PRIVATE | SMBV_SINGLESHARE;
+ spec->flags = flags;
+ return 0;
+}
+
+static void
+smb_usr_vcspec_free(struct smb_vcspec *spec)
+{
+ if (spec->sap)
+ smb_memfree(spec->sap);
+ if (spec->lap)
+ smb_memfree(spec->lap);
+}
+
+static int
+smb_usr_share2spec(struct smbioc_oshare *dp, struct smb_sharespec *spec)
+{
+ bzero(spec, sizeof(*spec));
+ spec->mode = dp->ioc_mode;
+ spec->rights = dp->ioc_rights;
+ spec->owner = dp->ioc_owner;
+ spec->group = dp->ioc_group;
+ spec->name = dp->ioc_share;
+ spec->stype = dp->ioc_stype;
+ spec->pass = dp->ioc_password;
+ return 0;
+}
+
+int
+smb_usr_lookup(struct smbioc_lookup *dp, struct smb_cred *scred,
+ struct smb_vc **vcpp, struct smb_share **sspp)
+{
+ struct smb_vc *vcp = NULL;
+ struct smb_vcspec vspec;
+ struct smb_sharespec sspec, *sspecp = NULL;
+ int error;
+
+ if (dp->ioc_level < SMBL_VC || dp->ioc_level > SMBL_SHARE)
+ return EINVAL;
+ error = smb_usr_vc2spec(&dp->ioc_ssn, &vspec);
+ if (error)
+ return error;
+ if (dp->ioc_flags & SMBLK_CREATE)
+ vspec.flags |= SMBV_CREATE;
+
+ if (dp->ioc_level >= SMBL_SHARE) {
+ error = smb_usr_share2spec(&dp->ioc_sh, &sspec);
+ if (error)
+ goto out;
+ sspecp = &sspec;
+ }
+ error = smb_sm_lookup(&vspec, sspecp, scred, &vcp);
+ if (error == 0) {
+ *vcpp = vcp;
+ *sspp = vspec.ssp;
+ }
+out:
+ smb_usr_vcspec_free(&vspec);
+ return error;
+}
+
+/*
+ * Connect to the resource specified by smbioc_ossn structure.
+ * It may either find an existing connection or try to establish a new one.
+ * If no errors occured smb_vc returned locked and referenced.
+ */
+int
+smb_usr_opensession(struct smbioc_ossn *dp, struct smb_cred *scred,
+ struct smb_vc **vcpp)
+{
+ struct smb_vc *vcp = NULL;
+ struct smb_vcspec vspec;
+ int error;
+
+ error = smb_usr_vc2spec(dp, &vspec);
+ if (error)
+ return error;
+ if (dp->ioc_opt & SMBVOPT_CREATE)
+ vspec.flags |= SMBV_CREATE;
+
+ error = smb_sm_lookup(&vspec, NULL, scred, &vcp);
+ smb_usr_vcspec_free(&vspec);
+ return error;
+}
+
+int
+smb_usr_openshare(struct smb_vc *vcp, struct smbioc_oshare *dp,
+ struct smb_cred *scred, struct smb_share **sspp)
+{
+ struct smb_share *ssp;
+ struct smb_sharespec shspec;
+ int error;
+
+ error = smb_usr_share2spec(dp, &shspec);
+ if (error)
+ return error;
+ error = smb_vc_lookupshare(vcp, &shspec, scred, &ssp);
+ if (error == 0) {
+ *sspp = ssp;
+ return 0;
+ }
+ if ((dp->ioc_opt & SMBSOPT_CREATE) == 0)
+ return error;
+ error = smb_share_create(vcp, &shspec, scred, &ssp);
+ if (error)
+ return error;
+ error = smb_smb_treeconnect(ssp, scred);
+ if (error) {
+ smb_share_put(ssp, scred);
+ } else
+ *sspp = ssp;
+ return error;
+}
+
+int
+smb_usr_simplerequest(struct smb_share *ssp, struct smbioc_rq *dp,
+ struct smb_cred *scred)
+{
+ struct smb_rq rq, *rqp = &rq;
+ struct mbchain *mbp;
+ struct mdchain *mdp;
+ u_int8_t wc;
+ u_int16_t bc;
+ int error;
+
+ switch (dp->ioc_cmd) {
+ case SMB_COM_TRANSACTION2:
+ case SMB_COM_TRANSACTION2_SECONDARY:
+ case SMB_COM_CLOSE_AND_TREE_DISC:
+ case SMB_COM_TREE_CONNECT:
+ case SMB_COM_TREE_DISCONNECT:
+ case SMB_COM_NEGOTIATE:
+ case SMB_COM_SESSION_SETUP_ANDX:
+ case SMB_COM_LOGOFF_ANDX:
+ case SMB_COM_TREE_CONNECT_ANDX:
+ return EPERM;
+ }
+ error = smb_rq_init(rqp, SSTOCP(ssp), dp->ioc_cmd, scred);
+ if (error)
+ return error;
+ mbp = &rqp->sr_rq;
+ smb_rq_wstart(rqp);
+ error = mb_put_mem(mbp, dp->ioc_twords, dp->ioc_twc * 2, MB_MUSER);
+ if (error)
+ goto bad;
+ smb_rq_wend(rqp);
+ smb_rq_bstart(rqp);
+ error = mb_put_mem(mbp, dp->ioc_tbytes, dp->ioc_tbc, MB_MUSER);
+ if (error)
+ goto bad;
+ smb_rq_bend(rqp);
+ error = smb_rq_simple(rqp);
+ if (error)
+ goto bad;
+ mdp = &rqp->sr_rp;
+ md_get_uint8(mdp, &wc);
+ dp->ioc_rwc = wc;
+ wc *= 2;
+ if (wc > dp->ioc_rpbufsz) {
+ error = EBADRPC;
+ goto bad;
+ }
+ error = md_get_mem(mdp, dp->ioc_rpbuf, wc, MB_MUSER);
+ if (error)
+ goto bad;
+ md_get_uint16le(mdp, &bc);
+ if ((wc + bc) > dp->ioc_rpbufsz) {
+ error = EBADRPC;
+ goto bad;
+ }
+ dp->ioc_rbc = bc;
+ error = md_get_mem(mdp, dp->ioc_rpbuf + wc, bc, MB_MUSER);
+bad:
+ dp->ioc_errclass = rqp->sr_errclass;
+ dp->ioc_serror = rqp->sr_serror;
+ dp->ioc_error = rqp->sr_error;
+ smb_rq_done(rqp);
+ return error;
+
+}
+
+static int
+smb_cpdatain(struct mbchain *mbp, int len, caddr_t data)
+{
+ int error;
+
+ if (len == 0)
+ return 0;
+ error = mb_init(mbp);
+ if (error)
+ return error;
+ return mb_put_mem(mbp, data, len, MB_MUSER);
+}
+
+int
+smb_usr_t2request(struct smb_share *ssp, struct smbioc_t2rq *dp,
+ struct smb_cred *scred)
+{
+ struct smb_t2rq t2, *t2p = &t2;
+ struct mdchain *mdp;
+ int error, len;
+
+ if (dp->ioc_tparamcnt > 0xffff || dp->ioc_tdatacnt > 0xffff ||
+ dp->ioc_setupcnt > 3)
+ return EINVAL;
+ error = smb_t2_init(t2p, SSTOCP(ssp), dp->ioc_setup[0], scred);
+ if (error)
+ return error;
+ len = t2p->t2_setupcount = dp->ioc_setupcnt;
+ if (len > 1)
+ t2p->t2_setupdata = dp->ioc_setup;
+ if (dp->ioc_name) {
+ t2p->t_name = smb_strdupin(dp->ioc_name, 128);
+ if (t2p->t_name == NULL) {
+ error = ENOMEM;
+ goto bad;
+ }
+ }
+ t2p->t2_maxscount = 0;
+ t2p->t2_maxpcount = dp->ioc_rparamcnt;
+ t2p->t2_maxdcount = dp->ioc_rdatacnt;
+ error = smb_cpdatain(&t2p->t2_tparam, dp->ioc_tparamcnt, dp->ioc_tparam);
+ if (error)
+ goto bad;
+ error = smb_cpdatain(&t2p->t2_tdata, dp->ioc_tdatacnt, dp->ioc_tdata);
+ if (error)
+ goto bad;
+ error = smb_t2_request(t2p);
+ if (error)
+ goto bad;
+ mdp = &t2p->t2_rparam;
+ if (mdp->md_top) {
+ len = m_fixhdr(mdp->md_top);
+ if (len > dp->ioc_rparamcnt) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ dp->ioc_rparamcnt = len;
+ error = md_get_mem(mdp, dp->ioc_rparam, len, MB_MUSER);
+ if (error)
+ goto bad;
+ } else
+ dp->ioc_rparamcnt = 0;
+ mdp = &t2p->t2_rdata;
+ if (mdp->md_top) {
+ len = m_fixhdr(mdp->md_top);
+ if (len > dp->ioc_rdatacnt) {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ dp->ioc_rdatacnt = len;
+ error = md_get_mem(mdp, dp->ioc_rdata, len, MB_MUSER);
+ } else
+ dp->ioc_rdatacnt = 0;
+bad:
+ if (t2p->t_name)
+ smb_strfree(t2p->t_name);
+ smb_t2_done(t2p);
+ return error;
+}
OpenPOWER on IntegriCloud