Mercurial > illumos > illumos-gate
view usr/src/lib/smbsrv/libsmbrdr/common/smbrdr_netbios.c @ 13328:2f33da224406
849 domain controller "hot fail over" can take forever
Reviewed by: Albert Lee <trisk@nexenta.com>
Reviewed by: Garrett D'Amore <garrett@nexenta.com>
Reviewed by: Richard Lowe <richlowe@richlowe.net>
Approved by: Richard Lowe <richlowe@richlowe.net>
author | Gordon Ross <gwr@nexenta.com> |
---|---|
date | Thu, 07 Apr 2011 19:44:19 -0400 |
parents | edb7861a1533 |
children |
line wrap: on
line source
/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2011 Nexenta Systems, Inc. All rights reserved. */ /* * NetBIOS support functions. NetBIOS is documented in the following * RFC documents: * * RFC 1001: Protocol Standard for a NetBIOS Service on a TCP/UDP * Transport: Concepts and Methods * * RFC 1002: Protocol Standard for a NetBIOS Service on a TCP/UDP * Transport: Detailed Specifications * */ #define BSD_BYTE_STRING_PROTOTYPES #include <string.h> #include <unistd.h> #include <synch.h> #include <sys/types.h> #include <sys/uio.h> #include <sys/time.h> #include <netinet/in.h> #include <stdio.h> #include <pthread.h> #include <smbsrv/smb.h> #define MAX_NETBIOS_NAME_SIZE 16 #define SESSION_MESSAGE 0x00 #define SESSION_REQUEST 0x81 #define POSITIVE_SESSION_RESPONSE 0x82 #define NEGATIVE_SESSION_RESPONSE 0x83 #define RETARGET_SESSION_RESPONSE 0x84 #define SESSION_KEEP_ALIVE 0x85 #define NB_READ_MSG_ERR_EOF 0 #define NB_READ_MSG_ERR -1 #define NB_READ_MSG_ERR_OVERFLOW -2 #define NB_READ_MSG_ERR_UNDERFLOW -3 #define NB_RCV_MSG_ERR_INVTYPE -4 int smbrdr_default_timeout = 45; /* sec. */ /* * Semaphore object used to serialize access through NetBIOS exchange. */ static mutex_t nb_mutex; static int nb_write_msg(int fd, unsigned char *buf, unsigned count, int type); static int nb_read_msg(int fd, unsigned char *buf, unsigned max_buf, int *type, long timeout); static int nb_read_itter(int fd, unsigned char *buf, unsigned cnt); static int nb_first_level_name_encode(char *name, char *scope, unsigned char *out, int max_out); /* * nb_lock * * Acquire mutex for doing netbios operations */ void nb_lock() { (void) mutex_lock(&nb_mutex); } /* * nb_lock * * Release netbios mutex. */ void nb_unlock() { (void) mutex_unlock(&nb_mutex); } void nb_close(int fd) { (void) mutex_lock(&nb_mutex); if (fd > 0) (void) close(fd); (void) mutex_unlock(&nb_mutex); } /* * nb_keep_alive * * Send the NetBIOS keep alive message only if smbrdr is connected on port 139. * No response is expected but we do need to ignore keep-alive messages in * nb_exchange. The mutex ensures compatibility/serialization with * nb_exchange to allow us to call this function from a separate thread. */ int nb_keep_alive(int fd, short port) { int nothing; int rc; if (port == IPPORT_SMB) return (0); (void) mutex_lock(&nb_mutex); rc = nb_write_msg(fd, (unsigned char *)¬hing, 0, SESSION_KEEP_ALIVE); (void) mutex_unlock(&nb_mutex); return (rc); } /* * nb_send * * This is just a wrapper round the nb_write_msg. */ int nb_send(int fd, unsigned char *send_buf, unsigned send_cnt) { int rc; rc = nb_write_msg(fd, send_buf, send_cnt, SESSION_MESSAGE); return (rc); } /* * nb_rcv * * This is a wrapper round the nb_read_msg() so that if a * keep-alive message is received, just discard it and go * back to look for the real response. */ int nb_rcv(int fd, unsigned char *recv_buf, unsigned recv_max, long timeout) { int rc; int type; do { rc = nb_read_msg(fd, recv_buf, recv_max, &type, timeout); if (rc < 0) return (rc); } while (type == SESSION_KEEP_ALIVE); if (type != SESSION_MESSAGE) return (NB_RCV_MSG_ERR_INVTYPE); return (rc); } /* * nb_exchange * * This is the NetBIOS workhorse function where we do the send/receive * message exchange. A mutex is used to serialize access because * we may get swapped out between the send and receive operations and * another thread could enter here and collect our response. If a * keep-alive message is received, just discard it and go back to look * for the real response. * * Note: With the addition of support for SMB over TCP, this function * may be exchanging NetBIOS-less SMB data. */ int nb_exchange(int fd, unsigned char *send_buf, unsigned send_cnt, unsigned char *recv_buf, unsigned recv_max, long timeout) { int rc; rc = nb_send(fd, send_buf, send_cnt); if (rc == send_cnt) rc = nb_rcv(fd, recv_buf, recv_max, timeout); return (rc); } /* * nb_session_request * * We should never see descriptor 0 (stdin) or -1. */ int nb_session_request(int fd, char *called_name, char *called_scope, char *calling_name, char *calling_scope) { unsigned char sr_buf[200]; int len; int rc; int type; if (fd == 0 || fd == -1) return (-1); rc = nb_first_level_name_encode(called_name, called_scope, sr_buf, 100); len = rc; rc = nb_first_level_name_encode(calling_name, calling_scope, sr_buf+len, 100); len += rc; (void) mutex_lock(&nb_mutex); rc = nb_write_msg(fd, (unsigned char *)sr_buf, len, SESSION_REQUEST); if (rc < 0) { (void) mutex_unlock(&nb_mutex); return (rc); } for (;;) { rc = nb_read_msg(fd, (unsigned char *)sr_buf, sizeof (sr_buf), &type, 0); if (rc < 0) { (void) mutex_unlock(&nb_mutex); return (rc); } if ((rc == 0) && (type == -1)) { (void) mutex_unlock(&nb_mutex); return (-1); /* EOF */ } if (type == POSITIVE_SESSION_RESPONSE) { (void) mutex_unlock(&nb_mutex); return (0); } if (type == NEGATIVE_SESSION_RESPONSE) { (void) mutex_unlock(&nb_mutex); return (-1); } } /* NOTREACHED */ (void) mutex_unlock(&nb_mutex); return (-1); } /* * nb_write_msg */ static int nb_write_msg(int fd, unsigned char *buf, unsigned count, int type) { struct iovec iov[2]; unsigned char header[4]; int rc; if (fd == 0 || fd == -1) { /* * We should never see descriptor 0 (stdin). */ return (-1); } /* * The NetBIOS message length is limited to 17 bits but * we use this layer for SMB over both NetBIOS and TCP * (NetBIOS-less SMB). When using SMB over TCP the length * is 24 bits but we are ignoring that for now because we * don't expect any messages larger than 64KB. */ header[0] = type; header[1] = (count >> 16) & 1; header[2] = count >> 8; header[3] = count; iov[0].iov_base = (caddr_t)header; iov[0].iov_len = 4; iov[1].iov_base = (caddr_t)buf; iov[1].iov_len = count; rc = writev(fd, iov, 2); if (rc != 4 + count) { return (-3); /* error */ } return (count); } /* * nb_read_msg * * Added select to ensure that we don't block forever waiting for a * message. */ static int nb_read_msg(int fd, unsigned char *buf, unsigned max_buf, int *type, long timeout) { unsigned char header[4]; int length; int rc; fd_set readfds; struct timeval tval; *type = -1; if (fd == 0 || fd == -1) { /* * We should never see descriptor 0 (stdin). */ return (NB_READ_MSG_ERR); } FD_ZERO(&readfds); FD_SET(fd, &readfds); if (timeout == 0) timeout = smbrdr_default_timeout; tval.tv_sec = timeout; tval.tv_usec = 0; if ((rc = select(fd + 1, &readfds, 0, 0, &tval)) <= 0) { return (NB_READ_MSG_ERR); } if ((rc = nb_read_itter(fd, header, 4)) < 0) return (rc); /* error */ if (rc != 4) return (NB_READ_MSG_ERR_EOF); /* EOF */ /* * The NetBIOS message length is limited to 17 bits but * we use this layer for SMB over both NetBIOS and TCP * (NetBIOS-less SMB). When using SMB over TCP the length * is 24 bits but we are ignoring that for now because we * don't expect any messages larger than 64KB. */ *type = header[0]; length = ((header[1]&1) << 16) + (header[2]<<8) + header[3]; if (length > max_buf) return (NB_READ_MSG_ERR_OVERFLOW); /* error overflow */ if ((rc = nb_read_itter(fd, buf, length)) != length) return (NB_READ_MSG_ERR_UNDERFLOW); /* error underflow */ return (rc); } /* * nb_read_itter * * We should never see descriptor 0 (stdin) or -1. */ static int nb_read_itter(int fd, unsigned char *buf, unsigned cnt) { int ix; int rc; for (ix = 0; ix < cnt; ix += rc) { if (fd == 0 || fd == -1) return (-1); if ((rc = read(fd, buf+ix, cnt-ix)) < 0) return (rc); if (rc == 0) break; } return (ix); } /* * nb_first_level_name_encode */ static int nb_first_level_name_encode(char *name, char *scope, unsigned char *out, int max_out) { unsigned char ch, len; unsigned char *in; unsigned char *lp; unsigned char *op = out; unsigned char *op_end = op + max_out; in = (unsigned char *)name; *op++ = 0x20; for (len = 0; ((ch = *in) != 0) && len < MAX_NETBIOS_NAME_SIZE; len++, in++) { *op++ = 'A' + ((ch >> 4) & 0xF); *op++ = 'A' + ((ch) & 0xF); } for (; len < MAX_NETBIOS_NAME_SIZE; len++) { ch = ' '; *op++ = 'A' + ((ch >> 4) & 0xF); *op++ = 'A' + ((ch) & 0xF); } in = (unsigned char *)scope; len = 0; lp = op++; for (; op < op_end; in++) { ch = *in; if (ch == 0) { if ((*lp = len) != 0) *op++ = 0; break; } if (ch == '.') { *lp = (op - lp) - 1; lp = op++; len = 0; } else { *op++ = ch; len++; } } return ((int)(op - out)); }