1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 #include <sys/param.h> 31 #include <sys/cpuvar.h> 32 #include <sys/ddi.h> 33 #include <sys/sunddi.h> 34 #include <sys/time.h> 35 #include <sys/varargs.h> 36 #include <sys/modctl.h> 37 #include <sys/pathname.h> 38 #include <sys/fs/snode.h> 39 #include <sys/fs/dv_node.h> 40 #include <sys/vnode.h> 41 #undef mem_free /* XXX Remove this after we convert everything to kmem_alloc */ 42 43 #include <smbsrv/smb_vops.h> 44 #include <smbsrv/smb.h> 45 #include <smbsrv/mlsvc.h> 46 #include <smbsrv/smb_kproto.h> 47 48 /* 49 * SMB Network Socket API 50 * 51 * smb_socreate: Creates an socket based on domain/type. 52 * smb_soshutdown: Disconnect a socket created with smb_socreate 53 * smb_sodestroy: Release resources associated with a socket 54 * smb_sosend: Send the contents of a buffer on a socket 55 * smb_sorecv: Receive data into a buffer from a socket 56 * smb_iov_sosend: Send the contents of an iovec on a socket 57 * smb_iov_sorecv: Receive data into an iovec from a socket 58 */ 59 60 struct sonode * 61 smb_socreate(int domain, int type, int protocol) 62 { 63 vnode_t *dvp = NULL; 64 vnode_t *vp = NULL; 65 struct snode *csp = NULL; 66 int err = 0; 67 major_t maj; 68 69 if ((vp = solookup(domain, type, protocol, NULL, &err)) == NULL) { 70 71 /* 72 * solookup calls sogetvp if the vp is not found in the cache. 73 * Since the call to sogetvp is hardwired to use USERSPACE 74 * and declared static we'll do the work here instead. 75 */ 76 err = lookupname(type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp", 77 UIO_SYSSPACE, FOLLOW, NULLVPP, &vp); 78 if (err) 79 return (NULL); 80 81 /* Check that it is the correct vnode */ 82 if (vp->v_type != VCHR) { 83 VN_RELE(vp); 84 return (NULL); 85 } 86 87 csp = VTOS(VTOS(vp)->s_commonvp); 88 if (!(csp->s_flag & SDIPSET)) { 89 char *pathname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 90 err = ddi_dev_pathname(vp->v_rdev, S_IFCHR, 91 pathname); 92 if (err == 0) { 93 err = devfs_lookupname(pathname, NULLVPP, 94 &dvp); 95 } 96 VN_RELE(vp); 97 kmem_free(pathname, MAXPATHLEN); 98 if (err != 0) { 99 return (NULL); 100 } 101 vp = dvp; 102 } 103 104 maj = getmajor(vp->v_rdev); 105 if (!STREAMSTAB(maj)) { 106 VN_RELE(vp); 107 return (NULL); 108 } 109 } 110 111 return (socreate(vp, domain, type, protocol, SOV_DEFAULT, NULL, &err)); 112 } 113 114 /* 115 * smb_soshutdown will disconnect the socket and prevent subsequent PDU 116 * reception and transmission. The sonode still exists but its state 117 * gets modified to indicate it is no longer connected. Calls to 118 * smb_sorecv/smb_iov_sorecv will return so smb_soshutdown can be used 119 * regain control of a thread stuck in smb_sorecv. 120 */ 121 void 122 smb_soshutdown(struct sonode *so) 123 { 124 (void) soshutdown(so, SHUT_RDWR); 125 } 126 127 /* 128 * smb_sodestroy releases all resources associated with a socket previously 129 * created with smb_socreate. The socket must be shutdown using smb_soshutdown 130 * before the socket is destroyed with smb_sodestroy, otherwise undefined 131 * behavior will result. 132 */ 133 void 134 smb_sodestroy(struct sonode *so) 135 { 136 vnode_t *vp = SOTOV(so); 137 138 (void) VOP_CLOSE(vp, 0, 1, 0, kcred, NULL); 139 VN_RELE(vp); 140 } 141 142 int 143 smb_sorecv(struct sonode *so, void *msg, size_t len) 144 { 145 iovec_t iov; 146 int err; 147 148 ASSERT(so != NULL); 149 ASSERT(len != 0); 150 151 /* 152 * Fill in iovec and receive data 153 */ 154 iov.iov_base = msg; 155 iov.iov_len = len; 156 157 if ((err = smb_iov_sorecv(so, &iov, 1, len)) != 0) { 158 return (err); 159 } 160 161 /* Successful receive */ 162 return (0); 163 } 164 165 /* 166 * smb_iov_sorecv - Receives an iovec from a connection 167 * 168 * This function gets the data asked for from the socket. It will return 169 * only when all the requested data has been retrieved or if an error 170 * occurs. 171 * 172 * Returns 0 for success, the socket errno value if sorecvmsg fails, and 173 * -1 if sorecvmsg returns success but uio_resid != 0 174 */ 175 int 176 smb_iov_sorecv(struct sonode *so, iovec_t *iop, int iovlen, size_t total_len) 177 { 178 struct msghdr msg; 179 struct uio uio; 180 int error; 181 182 ASSERT(iop != NULL); 183 184 /* Initialization of the message header. */ 185 bzero(&msg, sizeof (msg)); 186 msg.msg_iov = iop; 187 msg.msg_flags = MSG_WAITALL; 188 msg.msg_iovlen = iovlen; 189 190 /* Initialization of the uio structure. */ 191 bzero(&uio, sizeof (uio)); 192 uio.uio_iov = iop; 193 uio.uio_iovcnt = iovlen; 194 uio.uio_segflg = UIO_SYSSPACE; 195 uio.uio_resid = total_len; 196 197 if ((error = sorecvmsg(so, &msg, &uio)) == 0) { 198 /* Received data */ 199 if (uio.uio_resid == 0) { 200 /* All requested data received. Success */ 201 return (0); 202 } else { 203 /* Not all data was sent. Failure */ 204 return (-1); 205 } 206 } 207 208 /* Receive failed */ 209 return (error); 210 } 211 212 /* 213 * smb_net_txl_constructor 214 * 215 * Transmit list constructor 216 */ 217 void 218 smb_net_txl_constructor(smb_txlst_t *txl) 219 { 220 ASSERT(txl->tl_magic != SMB_TXLST_MAGIC); 221 222 mutex_init(&txl->tl_mutex, NULL, MUTEX_DEFAULT, NULL); 223 list_create(&txl->tl_list, sizeof (smb_txbuf_t), 224 offsetof(smb_txbuf_t, tb_lnd)); 225 txl->tl_active = B_FALSE; 226 txl->tl_magic = SMB_TXLST_MAGIC; 227 } 228 229 /* 230 * smb_net_txl_destructor 231 * 232 * Transmit list destructor 233 */ 234 void 235 smb_net_txl_destructor(smb_txlst_t *txl) 236 { 237 ASSERT(txl->tl_magic == SMB_TXLST_MAGIC); 238 239 txl->tl_magic = 0; 240 list_destroy(&txl->tl_list); 241 mutex_destroy(&txl->tl_mutex); 242 } 243 244 /* 245 * smb_net_txb_alloc 246 * 247 * Transmit buffer allocator 248 */ 249 smb_txbuf_t * 250 smb_net_txb_alloc(void) 251 { 252 smb_txbuf_t *txb; 253 254 txb = kmem_alloc(sizeof (smb_txbuf_t), KM_SLEEP); 255 256 bzero(&txb->tb_lnd, sizeof (txb->tb_lnd)); 257 txb->tb_len = 0; 258 txb->tb_magic = SMB_TXBUF_MAGIC; 259 260 return (txb); 261 } 262 263 /* 264 * smb_net_txb_free 265 * 266 * Transmit buffer deallocator 267 */ 268 void 269 smb_net_txb_free(smb_txbuf_t *txb) 270 { 271 ASSERT(txb->tb_magic == SMB_TXBUF_MAGIC); 272 ASSERT(!list_link_active(&txb->tb_lnd)); 273 274 txb->tb_magic = 0; 275 kmem_free(txb, sizeof (smb_txbuf_t)); 276 } 277 278 /* 279 * smb_net_txb_send 280 * 281 * This routine puts the transmit buffer passed in on the wire. If another 282 * thread is already draining the transmit list, the transmit buffer is 283 * queued and the routine returns immediately. 284 */ 285 int 286 smb_net_txb_send(struct sonode *so, smb_txlst_t *txl, smb_txbuf_t *txb) 287 { 288 list_t local; 289 int rc = 0; 290 iovec_t iov; 291 struct msghdr msg; 292 struct uio uio; 293 294 ASSERT(txl->tl_magic == SMB_TXLST_MAGIC); 295 296 mutex_enter(&txl->tl_mutex); 297 list_insert_tail(&txl->tl_list, txb); 298 if (txl->tl_active) { 299 mutex_exit(&txl->tl_mutex); 300 return (0); 301 } 302 303 txl->tl_active = B_TRUE; 304 list_create(&local, sizeof (smb_txbuf_t), 305 offsetof(smb_txbuf_t, tb_lnd)); 306 307 while (!list_is_empty(&txl->tl_list)) { 308 list_move_tail(&local, &txl->tl_list); 309 mutex_exit(&txl->tl_mutex); 310 while ((txb = list_head(&local)) != NULL) { 311 ASSERT(txb->tb_magic == SMB_TXBUF_MAGIC); 312 list_remove(&local, txb); 313 314 iov.iov_base = (void *)txb->tb_data; 315 iov.iov_len = txb->tb_len; 316 317 bzero(&msg, sizeof (msg)); 318 msg.msg_iov = &iov; 319 msg.msg_flags = MSG_WAITALL; 320 msg.msg_iovlen = 1; 321 322 bzero(&uio, sizeof (uio)); 323 uio.uio_iov = &iov; 324 uio.uio_iovcnt = 1; 325 uio.uio_segflg = UIO_SYSSPACE; 326 uio.uio_resid = txb->tb_len; 327 328 rc = sosendmsg(so, &msg, &uio); 329 330 smb_net_txb_free(txb); 331 332 if ((rc == 0) && (uio.uio_resid == 0)) 333 continue; 334 335 if (rc == 0) 336 rc = -1; 337 338 while ((txb = list_head(&local)) != NULL) { 339 ASSERT(txb->tb_magic == SMB_TXBUF_MAGIC); 340 list_remove(&local, txb); 341 smb_net_txb_free(txb); 342 } 343 break; 344 } 345 mutex_enter(&txl->tl_mutex); 346 347 if (rc == 0) 348 continue; 349 350 while ((txb = list_head(&txl->tl_list)) != NULL) { 351 ASSERT(txb->tb_magic == SMB_TXBUF_MAGIC); 352 list_remove(&txl->tl_list, txb); 353 smb_net_txb_free(txb); 354 } 355 break; 356 } 357 txl->tl_active = B_FALSE; 358 mutex_exit(&txl->tl_mutex); 359 360 return (rc); 361 } 362