/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Description: * * Contains base code for netbios name service. * * * 6. DEFINED CONSTANTS AND VARIABLES * * GENERAL: * * SCOPE_ID The name of the NetBIOS scope. * * This is expressed as a character * string meeting the requirements of * the domain name system and without * a leading or trailing "dot". * * An implementation may elect to make * this a single global value for the * node or allow it to be specified * with each separate NetBIOS name * (thus permitting cross-scope * references.) * * BROADCAST_ADDRESS An IP address composed of the * nodes's network and subnetwork * numbers with all remaining bits set * to one. * * I.e. "Specific subnet" broadcast * addressing according to section 2.3 * of RFC 950. * * BCAST_REQ_RETRY_TIMEOUT 250 milliseconds. * An adaptive timer may be used. * * BCAST_REQ_RETRY_COUNT 3 * * UCAST_REQ_RETRY_TIMEOUT 5 seconds * An adaptive timer may be used. * * UCAST_REQ_RETRY_COUNT 3 * * MAX_DATAGRAM_LENGTH 576 bytes (default) * * * NAME SERVICE: * * REFRESH_TIMER Negotiated with NAME for each name. * * CONFLICT_TIMER 1 second * Implementations may chose a longer * value. * * * NAME_SERVICE_TCP_PORT 137 (decimal) * * NAME_SERVICE_UDP_PORT 137 (decimal) * * INFINITE_TTL 0 */ #include #include #include #include #include #include #include #include #include #include #include #include #define NAME_HEADER_SIZE 12 typedef struct name_reply { struct name_reply *forw; struct name_reply *back; struct name_packet *packet; struct addr_entry *addr; unsigned short name_trn_id; unsigned short flags; } name_reply; static struct name_reply reply_queue; static mutex_t rq_mtx; static mutex_t reply_mtx; static cond_t reply_cv; static name_queue_t delete_queue; static name_queue_t refresh_queue; /* * Flag to control whether or not NetBIOS name refresh requests * are logged. Set to non-zero to enable logging. */ static unsigned short netbios_name_transcation_id = 1; static int name_sock = 0; static int bcast_num = 0; static int nbns_num = 0; static struct addr_entry smb_bcast_list[SMB_PI_MAX_NETWORKS]; static struct addr_entry smb_nbns[SMB_PI_MAX_WINS]; static int smb_netbios_process_response(unsigned short, struct addr_entry *, struct name_packet *, uint32_t); static int smb_send_name_service_packet(struct addr_entry *addr, struct name_packet *packet); static int smb_end_node_challenge(struct name_reply *reply_info) { int rc; uint32_t retry; unsigned short tid; struct resource_record *answer; struct name_question question; struct addr_entry *addr; struct name_entry *destination; struct name_packet packet; struct timespec st; /* * The response packet has in it the address of the presumed owner * of the name. Challenge that owner. If owner either does not * respond or indicates that he no longer owns the name, claim the * name. Otherwise, the name cannot be claimed. */ if ((answer = reply_info->packet->answer) == 0) return (-1); destination = answer->name; question.name = answer->name; packet.info = NAME_QUERY_REQUEST | NM_FLAGS_UNICAST; packet.qdcount = 1; /* question entries */ packet.question = &question; packet.ancount = 0; /* answer recs */ packet.answer = NULL; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 0; /* additional recs */ packet.additional = NULL; addr = &destination->addr_list; for (retry = 0; retry < UCAST_REQ_RETRY_COUNT; retry++) { tid = netbios_name_transcation_id++; packet.name_trn_id = tid; if (smb_send_name_service_packet(addr, &packet) >= 0) { if ((rc = smb_netbios_process_response(tid, addr, &packet, UCAST_REQ_RETRY_TIMEOUT)) != 0) return (rc); } st.tv_sec = 0; st.tv_nsec = (UCAST_REQ_RETRY_TIMEOUT * 1000000); (void) nanosleep(&st, 0); } /* No reply */ return (0); } static struct name_reply * smb_name_get_reply(unsigned short tid, uint32_t timeout) { unsigned short info; struct resource_record *answer; struct name_reply *reply; uint32_t wait_time, to_save; /* in millisecond */ struct timeval wt; timestruc_t to; to_save = timeout; reply = (struct name_reply *)malloc(sizeof (struct name_reply)); if (reply != 0) { reply->flags = 0; reply->name_trn_id = tid; (void) mutex_lock(&rq_mtx); QUEUE_INSERT_TAIL(&reply_queue, reply); (void) mutex_unlock(&rq_mtx); for (;;) { (void) gettimeofday(&wt, 0); wait_time = wt.tv_usec / 1000; (void) mutex_lock(&reply_mtx); to.tv_sec = 0; to.tv_nsec = timeout * 1000000; (void) cond_reltimedwait(&reply_cv, &reply_mtx, &to); (void) mutex_unlock(&reply_mtx); if (reply->flags != 0) { info = reply->packet->info; if (PACKET_TYPE(info) == WACK_RESPONSE) { answer = reply->packet->answer; wait_time = (answer) ? TO_MILLISECONDS(answer->ttl) : DEFAULT_TTL; free(reply->addr); free(reply->packet); timeout = to_save + wait_time; reply->flags = 0; reply->name_trn_id = tid; (void) mutex_lock(&rq_mtx); QUEUE_INSERT_TAIL(&reply_queue, reply); (void) mutex_unlock(&rq_mtx); continue; } return (reply); } (void) gettimeofday(&wt, 0); wait_time = (wt.tv_usec / 1000) - wait_time; if (wait_time >= timeout) { (void) mutex_lock(&rq_mtx); QUEUE_CLIP(reply); (void) mutex_unlock(&rq_mtx); free(reply); break; } timeout -= wait_time; } } return (0); } static void smb_reply_ready(struct name_packet *packet, struct addr_entry *addr) { struct name_reply *reply; struct resource_record *answer; (void) mutex_lock(&rq_mtx); for (reply = reply_queue.forw; reply != &reply_queue; reply = reply->forw) { if (reply->name_trn_id == packet->name_trn_id) { QUEUE_CLIP(reply); (void) mutex_unlock(&rq_mtx); reply->addr = addr; reply->packet = packet; (void) mutex_lock(&reply_mtx); reply->flags |= 0x0001; /* reply ready */ (void) cond_signal(&reply_cv); (void) mutex_unlock(&reply_mtx); return; } } (void) mutex_unlock(&rq_mtx); /* Presumably nobody is waiting any more... */ free(addr); answer = packet->answer; if (answer) smb_netbios_name_freeaddrs(answer->name); free(packet); } static int smb_netbios_process_response(unsigned short tid, struct addr_entry *addr, struct name_packet *packet, uint32_t timeout) { int rc = 0; unsigned short info; struct name_reply *reply; struct resource_record *answer; struct name_entry *name; struct name_entry *entry; struct name_question *question; uint32_t ttl; if ((reply = smb_name_get_reply(tid, timeout)) == 0) { return (0); /* No reply: retry */ } info = reply->packet->info; answer = reply->packet->answer; /* response */ switch (PACKET_TYPE(info)) { case NAME_QUERY_RESPONSE: if (POSITIVE_RESPONSE(info)) { addr = &answer->name->addr_list; do { /* * Make sure that remote name is not * flagged local */ addr->attributes &= ~NAME_ATTR_LOCAL; addr->refresh_ttl = addr->ttl = (answer && answer->ttl) ? (answer->ttl >> 1) : TO_SECONDS(DEFAULT_TTL); addr = addr->forw; } while (addr != &answer->name->addr_list); smb_netbios_name_dump(answer->name); (void) smb_netbios_cache_insert_list(answer->name); rc = 1; } else { rc = -1; } break; case NAME_REGISTRATION_RESPONSE: if (NEGATIVE_RESPONSE(info)) { if (RCODE(info) == RCODE_CFT_ERR) { if (answer == 0) { rc = -RCODE(info); break; } name = answer->name; entry = smb_netbios_cache_lookup(name); if (entry) { /* * a name in the state "conflict * detected" does not "logically" exist * on that node. No further session * will be accepted on that name. * No datagrams can be sent against * that name. * Such an entry will not be used for * purposes of processing incoming * request packets. * The only valid user NetBIOS operation * against such a name is DELETE NAME. */ entry->attributes |= NAME_ATTR_CONFLICT; syslog(LOG_DEBUG, "NETBIOS Name conflict: %15.15s", entry->name); smb_netbios_cache_unlock_entry(entry); } } rc = -RCODE(info); break; } /* * name can be added: * adjust refresh timeout value, * TTL, for this name */ question = packet->question; ttl = (answer && answer->ttl) ? answer->ttl >> 1 : TO_SECONDS(DEFAULT_TTL); if ((entry = smb_netbios_cache_lookup(question->name)) != 0) { addr = &entry->addr_list; do { if ((addr->refresh_ttl == 0) || (ttl < addr->refresh_ttl)) addr->refresh_ttl = addr->ttl = ttl; addr = addr->forw; } while (addr != &entry->addr_list); smb_netbios_cache_unlock_entry(entry); } rc = 1; break; case NAME_RELEASE_RESPONSE: rc = 1; break; case END_NODE_CHALLENGE_REGISTRATION_REQUEST: /* * The response packet has in it the * address of the presumed owner of the * name. Challenge that owner. If * owner either does not respond or * indicates that he no longer owns the * name, claim the name. Otherwise, * the name cannot be claimed. */ rc = smb_end_node_challenge(reply); break; default: rc = 0; break; } if (answer) smb_netbios_name_freeaddrs(answer->name); free(reply->addr); free(reply->packet); free(reply); return (rc); /* retry */ } /* * smb_name_buf_from_packet * * Description: * Convert a NetBIOS Name Server Packet Block (npb) * into the bits and bytes destined for the wire. * The "buf" is used as a heap. * * Inputs: * char * buf -> Buffer, from the wire * unsigned n_buf -> Length of 'buf' * name_packet *npb -> Packet block, decode into * unsigned n_npb -> Max bytes in 'npb' * * Returns: * >0 -> Encode successful, value is length of packet in "buf" * -1 -> Hard error, can not possibly encode * -2 -> Need more memory in buf -- it's too small */ static int smb_name_buf_from_packet(unsigned char *buf, int n_buf, struct name_packet *npb) { struct addr_entry *raddr; unsigned char *heap = buf; unsigned char *end_heap = heap + n_buf; unsigned char *dnptrs[32]; unsigned char comp_name_buf[MAX_NAME_LENGTH]; unsigned int tmp; int i, step; if (n_buf < NAME_HEADER_SIZE) return (-1); /* no header, impossible */ dnptrs[0] = heap; dnptrs[1] = 0; BE_OUT16(heap, npb->name_trn_id); heap += 2; BE_OUT16(heap, npb->info); heap += 2; BE_OUT16(heap, npb->qdcount); heap += 2; BE_OUT16(heap, npb->ancount); heap += 2; BE_OUT16(heap, npb->nscount); heap += 2; BE_OUT16(heap, npb->arcount); heap += 2; for (i = 0; i < npb->qdcount; i++) { if ((heap + 34 + 4) > end_heap) return (-2); (void) smb_first_level_name_encode(npb->question[i].name, comp_name_buf, sizeof (comp_name_buf)); (void) strcpy((char *)heap, (char *)comp_name_buf); heap += strlen((char *)comp_name_buf) + 1; BE_OUT16(heap, npb->question[i].question_type); heap += 2; BE_OUT16(heap, npb->question[i].question_class); heap += 2; } for (step = 1; step <= 3; step++) { struct resource_record *nrr; int n; /* truly ugly, but saves code copying */ if (step == 1) { n = npb->ancount; nrr = npb->answer; } else if (step == 2) { n = npb->nscount; nrr = npb->authority; } else { /* step == 3 */ n = npb->arcount; nrr = npb->additional; } for (i = 0; i < n; i++) { if ((heap + 34 + 10) > end_heap) return (-2); (void) smb_first_level_name_encode(nrr->name, comp_name_buf, sizeof (comp_name_buf)); (void) strcpy((char *)heap, (char *)comp_name_buf); heap += strlen((char *)comp_name_buf) + 1; BE_OUT16(heap, nrr[i].rr_type); heap += 2; BE_OUT16(heap, nrr[i].rr_class); heap += 2; BE_OUT32(heap, nrr[i].ttl); heap += 4; BE_OUT16(heap, nrr[i].rdlength); heap += 2; if ((tmp = nrr[i].rdlength) > 0) { if ((heap + tmp) > end_heap) return (-2); if (nrr[i].rr_type == NAME_RR_TYPE_NB && nrr[i].rr_class == NAME_RR_CLASS_IN && tmp >= 6 && nrr[i].rdata == 0) { tmp = nrr[i].name->attributes & (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); BE_OUT16(heap, tmp); heap += 2; raddr = &nrr[i].name->addr_list; (void) memcpy(heap, &raddr->sin.sin_addr.s_addr, sizeof (uint32_t)); heap += 4; } else { bcopy(nrr[i].rdata, heap, tmp); heap += tmp; } } } } return (heap - buf); } /* * strnchr * * Lookup for character 'c' in first 'n' chars of string 's'. * Returns pointer to the found char, otherwise returns 0. */ static char * strnchr(const char *s, char c, int n) { char *ps = (char *)s; char *es = (char *)s + n; while (ps < es && *ps) { if (*ps == c) return (ps); ++ps; } if (*ps == '\0' && c == '\0') return (ps); return (0); } static boolean_t is_multihome(char *name) { return (smb_nic_getnum(name) > 1); } /* * smb_netbios_getname * * Get the Netbios name part of the given record. * Does some boundary checks. * * Returns the name length on success, otherwise * returns 0. */ static int smb_netbios_getname(char *name, char *buf, char *buf_end) { char *name_end; int name_len; if (buf >= buf_end) { /* no room for a NB name */ return (0); } name_end = strnchr(buf, '\0', buf_end - buf + 1); if (name_end == 0) { /* not a valid NB name */ return (0); } name_len = name_end - buf + 1; (void) strlcpy(name, buf, name_len); return (name_len); } /* * smb_name_buf_to_packet * * Description: * Convert the bits and bytes that came from the wire * into a NetBIOS Name Server Packet Block (npb). * The "block" is used as a heap. * * Inputs: * char * buf -> Buffer, from the wire * int n_buf -> Length of 'buf' * name_packet *npb -> Packet block, decode into * int n_npb -> Max bytes in 'npb' * * Returns: * >0 -> Decode (parse) successful, value is byte length of npb * -1 -> Hard error, can not possibly decode * -2 -> Need more memory in npb -- it's too small */ static struct name_packet * smb_name_buf_to_packet(char *buf, int n_buf) { struct name_packet *npb; unsigned char *heap; unsigned char *scan = (unsigned char *)buf; unsigned char *scan_end = scan + n_buf; char name_buf[MAX_NAME_LENGTH]; struct resource_record *nrr = 0; int rc, i, n, nn, ns; unsigned short name_trn_id, info; unsigned short qdcount, ancount, nscount, arcount; struct addr_entry *next; int name_len; if (n_buf < NAME_HEADER_SIZE) { /* truncated header */ syslog(LOG_DEBUG, "SmbNBNS: packet is too short (%d)", n_buf); return (0); } name_trn_id = BE_IN16(scan); scan += 2; info = BE_IN16(scan); scan += 2; qdcount = BE_IN16(scan); scan += 2; ancount = BE_IN16(scan); scan += 2; nscount = BE_IN16(scan); scan += 2; arcount = BE_IN16(scan); scan += 2; ns = sizeof (struct name_entry); n = n_buf + sizeof (struct name_packet) + ((unsigned)qdcount * (sizeof (struct name_question) + ns)) + ((unsigned)ancount * (sizeof (struct resource_record) + ns)) + ((unsigned)nscount * (sizeof (struct resource_record) + ns)) + ((unsigned)arcount * (sizeof (struct resource_record) + ns)); if ((npb = (struct name_packet *)malloc(n)) == 0) { return (0); } bzero(npb, n); heap = npb->block_data; npb->name_trn_id = name_trn_id; npb->info = info; npb->qdcount = qdcount; npb->ancount = ancount; npb->nscount = nscount; npb->arcount = arcount; /* scan is in position for question entries */ /* * Measure the space needed for the tables */ if (qdcount > 0) { /* LINTED - E_BAD_PTR_CAST_ALIGN */ npb->question = (struct name_question *)heap; heap += qdcount * sizeof (struct name_question); for (i = 0; i < qdcount; i++) { /* LINTED - E_BAD_PTR_CAST_ALIGN */ npb->question[i].name = (struct name_entry *)heap; heap += sizeof (struct name_entry); } } /* LINTED - E_BAD_PTR_CAST_ALIGN */ nrr = (struct resource_record *)heap; if (ancount > 0) { /* LINTED - E_BAD_PTR_CAST_ALIGN */ npb->answer = (struct resource_record *)heap; heap += ancount * sizeof (struct resource_record); } if (nscount > 0) { /* LINTED - E_BAD_PTR_CAST_ALIGN */ npb->authority = (struct resource_record *)heap; heap += nscount * sizeof (struct resource_record); } if (arcount > 0) { /* LINTED - E_BAD_PTR_CAST_ALIGN */ npb->additional = (struct resource_record *)heap; heap += arcount * sizeof (struct resource_record); } /* * Populate each resource_record's .name field. * Done as a second pass so that all resource records * (answer, authority, additional) are consecutive via nrr[i]. */ for (i = 0; i < (ancount + nscount + arcount); i++) { /* LINTED - E_BAD_PTR_CAST_ALIGN */ nrr[i].name = (struct name_entry *)heap; heap += sizeof (struct name_entry); } for (i = 0; i < npb->qdcount; i++) { name_len = smb_netbios_getname(name_buf, (char *)scan, (char *)scan_end); if (name_len <= 0) { free(npb); return (0); } smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, 0, 0, 0, 0, 0, npb->question[i].name); rc = smb_first_level_name_decode((unsigned char *)name_buf, npb->question[i].name); if (rc < 0) { /* Couldn't decode the question name */ free(npb); return (0); } scan += name_len; if (scan + 4 > scan_end) { /* no room for Question Type(2) and Class(2) fields */ free(npb); return (0); } npb->question[i].question_type = BE_IN16(scan); scan += 2; npb->question[i].question_class = BE_IN16(scan); scan += 2; } /* * Cheat. Remaining sections are of the same resource_record * format. Table space is consecutive. */ for (i = 0; i < (ancount + nscount + arcount); i++) { if (scan[0] == 0xc0) { /* Namebuf is reused... */ rc = 2; } else { name_len = smb_netbios_getname(name_buf, (char *)scan, (char *)scan_end); if (name_len <= 0) { free(npb); return (0); } rc = name_len; } scan += rc; if (scan + 10 > scan_end) { /* * no room for RR_TYPE (2), RR_CLASS (2), TTL (4) and * RDLENGTH (2) fields. */ free(npb); return (0); } smb_init_name_struct(NETBIOS_EMPTY_NAME, 0, 0, 0, 0, 0, 0, nrr[i].name); if ((rc = smb_first_level_name_decode((unsigned char *)name_buf, nrr[i].name)) < 0) { free(npb); return (0); } nrr[i].rr_type = BE_IN16(scan); scan += 2; nrr[i].rr_class = BE_IN16(scan); scan += 2; nrr[i].ttl = BE_IN32(scan); scan += 4; nrr[i].rdlength = BE_IN16(scan); scan += 2; if ((n = nrr[i].rdlength) > 0) { if ((scan + n) > scan_end) { /* no room for RDATA */ free(npb); return (0); } bcopy(scan, heap, n); nn = n; if (nrr[i].rr_type == 0x0020 && nrr[i].rr_class == 0x01 && n >= 6) { while (nn) { if (nn == 6) next = &nrr[i].name->addr_list; else { next = (struct addr_entry *) malloc( sizeof (struct addr_entry)); if (next == 0) { /* not enough memory */ free(npb); return (0); } QUEUE_INSERT_TAIL( &nrr[i].name->addr_list, next); } nrr[i].name->attributes = BE_IN16(scan); next->sin.sin_family = AF_INET; next->sinlen = sizeof (next->sin); (void) memcpy( &next->sin.sin_addr.s_addr, scan + 2, sizeof (uint32_t)); next->sin.sin_port = htons(DGM_SRVC_UDP_PORT); nn -= 6; scan += 6; } } else { nrr[i].rdata = heap; scan += n; } heap += n; } } return (npb); } /* * smb_send_name_service_packet * * Description: * * Send out a name service packet to proper destination. * * Inputs: * struct netbios_name *dest -> NETBIOS name of destination * struct name_packet *packet -> Packet to send * * Returns: * success -> >0 * failure -> <=0 */ static int smb_send_name_service_packet(struct addr_entry *addr, struct name_packet *packet) { unsigned char buf[MAX_DATAGRAM_LENGTH]; int len; if ((len = smb_name_buf_from_packet(buf, sizeof (buf), packet)) < 0) { errno = EINVAL; return (-1); } return (sendto(name_sock, buf, len, MSG_EOR, (struct sockaddr *)&addr->sin, addr->sinlen)); } /* * 4.2.1.1. HEADER * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID | OPCODE | NM_FLAGS | RCODE | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | QDCOUNT | ANCOUNT | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NSCOUNT | ARCOUNT | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Field Description * * NAME_TRN_ID Transaction ID for Name Service Transaction. * Requester places a unique value for each active * transaction. Responder puts NAME_TRN_ID value * from request packet in response packet. * * OPCODE Packet type code, see table below. * * NM_FLAGS Flags for operation, see table below. * * RCODE Result codes of request. Table of RCODE values * for each response packet below. * * QDCOUNT Unsigned 16 bit integer specifying the number of * entries in the question section of a Name * * Service packet. Always zero (0) for responses. * Must be non-zero for all NetBIOS Name requests. * * ANCOUNT Unsigned 16 bit integer specifying the number of * resource records in the answer section of a Name * Service packet. * * NSCOUNT Unsigned 16 bit integer specifying the number of * resource records in the authority section of a * Name Service packet. * * ARCOUNT Unsigned 16 bit integer specifying the number of * resource records in the additional records * section of a Name Service packet. * * The OPCODE field is defined as: * * 0 1 2 3 4 * +---+---+---+---+---+ * | R | OPCODE | * +---+---+---+---+---+ * * Symbol Bit(s) Description * * OPCODE 1-4 Operation specifier: * 0 = query * 5 = registration * 6 = release * 7 = WACK * 8 = refresh * * R 0 RESPONSE flag: * if bit == 0 then request packet * if bit == 1 then response packet. */ /* * The NM_FLAGS field is defined as: * * * 0 1 2 3 4 5 6 * +---+---+---+---+---+---+---+ * |AA |TC |RD |RA | 0 | 0 | B | * +---+---+---+---+---+---+---+ * * Symbol Bit(s) Description * * B 6 Broadcast Flag. * = 1: packet was broadcast or multicast * = 0: unicast * * RA 3 Recursion Available Flag. * * Only valid in responses from a NetBIOS Name * Server -- must be zero in all other * responses. * * If one (1) then the NAME supports recursive * query, registration, and release. * * If zero (0) then the end-node must iterate * for query and challenge for registration. * * RD 2 Recursion Desired Flag. * * May only be set on a request to a NetBIOS * Name Server. * * The NAME will copy its state into the * response packet. * * If one (1) the NAME will iterate on the * query, registration, or release. * * TC 1 Truncation Flag. * * Set if this message was truncated because the * datagram carrying it would be greater than * 576 bytes in length. Use TCP to get the * information from the NetBIOS Name Server. * * AA 0 Authoritative Answer flag. * * Must be zero (0) if R flag of OPCODE is zero * (0). * * If R flag is one (1) then if AA is one (1) * then the node responding is an authority for * the domain name. * * End nodes responding to queries always set * this bit in responses. * */ /* * 4.2.1.2. QUESTION SECTION * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / QUESTION_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | QUESTION_TYPE | QUESTION_CLASS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Field Description * * QUESTION_NAME The compressed name representation of the * NetBIOS name for the request. * * QUESTION_TYPE The type of request. The values for this field * are specified for each request. * * QUESTION_CLASS The class of the request. The values for this * field are specified for each request. * * QUESTION_TYPE is defined as: * * Symbol Value Description: * * NB 0x0020 NetBIOS general Name Service Resource Record * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE * STATUS REQUEST) * * QUESTION_CLASS is defined as: * * Symbol Value Description: * * IN 0x0001 Internet class */ #define QUESTION_TYPE_NETBIOS_GENERAL 0x20 #define QUESTION_TYPE_NETBIOS_STATUS 0x21 #define QUESTION_CLASS_INTERNET 0x0001 /* * * 4.2.1.3. RESOURCE RECORD * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | RR_TYPE | RR_CLASS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | RDLENGTH | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | * / / * / RDATA / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Field Description * * RR_NAME The compressed name representation of the * NetBIOS name corresponding to this resource * record. * * RR_TYPE Resource record type code * * RR_CLASS Resource record class code * * TTL The Time To Live of a the resource record's * name. * * RDLENGTH Unsigned 16 bit integer that specifies the * number of bytes in the RDATA field. * * RDATA RR_CLASS and RR_TYPE dependent field. Contains * the resource information for the NetBIOS name. * * RESOURCE RECORD RR_TYPE field definitions: * * Symbol Value Description: * * A 0x0001 IP address Resource Record (See REDIRECT NAME * QUERY RESPONSE) * NS 0x0002 Name Server Resource Record (See REDIRECT * NAME QUERY RESPONSE) * NULL 0x000A NULL Resource Record (See WAIT FOR * ACKNOWLEDGEMENT RESPONSE) * NB 0x0020 NetBIOS general Name Service Resource Record * (See NB_FLAGS and NB_ADDRESS, below) * NBSTAT 0x0021 NetBIOS NODE STATUS Resource Record (See NODE * STATUS RESPONSE) */ #define RR_TYPE_IP_ADDRESS_RESOURCE 0x0001 #define RR_TYPE_NAME_SERVER_RESOURCE 0x0002 #define RR_TYPE_NULL_RESOURCE 0x000A #define RR_TYPE_NETBIOS_RESOURCE 0x0020 #define RR_TYPE_NETBIOS_STATUS 0x0021 /* * * RESOURCE RECORD RR_CLASS field definitions: * * Symbol Value Description: * * IN 0x0001 Internet class */ #define RR_CLASS_INTERNET_CLASS 0x0001 /* * * NB_FLAGS field of the RESOURCE RECORD RDATA field for RR_TYPE of * "NB": * * 1 1 1 1 1 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * | G | ONT | RESERVED | * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * * Symbol Bit(s) Description: * * RESERVED 3-15 Reserved for future use. Must be zero (0). * ONT 1,2 Owner Node Type: * 00 = B node * 01 = P node * 10 = M node * 11 = Reserved for future use * For registration requests this is the * claimant's type. * For responses this is the actual owner's * type. * * G 0 Group Name Flag. * If one (1) then the RR_NAME is a GROUP * NetBIOS name. * If zero (0) then the RR_NAME is a UNIQUE * NetBIOS name. * * The NB_ADDRESS field of the RESOURCE RECORD RDATA field for * RR_TYPE of "NB" is the IP address of the name's owner. * */ #define RR_FLAGS_NB_ONT_MASK 0x6000 #define RR_FLAGS_NB_ONT_B_NODE 0x0000 #define RR_FLAGS_NB_ONT_P_NODE 0x2000 #define RR_FLAGS_NB_ONT_M_NODE 0x4000 #define RR_FLAGS_NB_ONT_RESERVED 0x6000 #define RR_FLAGS_NB_GROUP_NAME 0x8000 /* * smb_netbios_send_rcv * * This function sends the given NetBIOS packet to the given * address and get back the response. If send operation is not * successful, it's repeated 'retries' times. * * Returns: * 0 Unsuccessful send operation; no reply * 1 Got reply */ static int smb_netbios_send_rcv(int bcast, struct addr_entry *destination, struct name_packet *packet, uint32_t retries, uint32_t timeout) { uint32_t retry; unsigned short tid; struct timespec st; int rc; for (retry = 0; retry < retries; retry++) { if ((destination->flags & ADDR_FLAG_VALID) == 0) return (0); tid = netbios_name_transcation_id++; packet->name_trn_id = tid; if (smb_send_name_service_packet(destination, packet) >= 0) { rc = smb_netbios_process_response(tid, destination, packet, timeout); if ((rc > 0) || (bcast == BROADCAST)) return (1); if (rc != 0) return (0); } st.tv_sec = 0; st.tv_nsec = (timeout * 1000000); (void) nanosleep(&st, 0); } return (0); } /* * 4.2.2. NAME REGISTRATION REQUEST * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |0| 0x5 |0|0|1|0|0 0|B| 0x0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0001 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / QUESTION_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0006 | NB_FLAGS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_ADDRESS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Since the RR_NAME is the same name as the QUESTION_NAME, the * RR_NAME representation must use pointers to the QUESTION_NAME * name's labels to guarantee the length of the datagram is less * than the maximum 576 bytes. See section above on name formats * and also page 31 and 32 of RFC 883, Domain Names - Implementation * and Specification, for a complete description of compressed name * label pointers. */ static int smb_send_name_registration_request(int bcast, struct name_question *question, struct resource_record *additional) { int gotreply = 0; uint32_t retries; uint32_t timeout; struct addr_entry *destination; struct name_packet packet; unsigned char type; int i, addr_num, rc; type = question->name->name[15]; if ((type != 0x00) && (type != 0x20)) { syslog(LOG_ERR, "netbios: error trying to register" " non-local name"); smb_netbios_name_logf(question->name); question->name->attributes &= ~NAME_ATTR_LOCAL; return (-1); } if (bcast == BROADCAST) { if (bcast_num == 0) return (0); destination = smb_bcast_list; addr_num = bcast_num; retries = BCAST_REQ_RETRY_COUNT; timeout = BCAST_REQ_RETRY_TIMEOUT; packet.info = NAME_REGISTRATION_REQUEST | NM_FLAGS_BROADCAST; } else { if (nbns_num == 0) return (0); destination = smb_nbns; addr_num = nbns_num; retries = UCAST_REQ_RETRY_COUNT; timeout = UCAST_REQ_RETRY_TIMEOUT; packet.info = NAME_REGISTRATION_REQUEST | NM_FLAGS_UNICAST; } packet.qdcount = 1; /* question entries */ packet.question = question; packet.ancount = 0; /* answer recs */ packet.answer = NULL; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 1; /* additional recs */ packet.additional = additional; if (IS_UNIQUE(question->name->attributes) && (is_multihome((char *)(question->name->name)))) packet.info |= NAME_MULTIHOME_REGISTRATION_REQUEST; for (i = 0; i < addr_num; i++) { /* * Only register with the Primary WINS server, * unless we got no reply. */ if ((bcast == UNICAST) && gotreply) break; rc = smb_netbios_send_rcv(bcast, &destination[i], &packet, retries, timeout); if (rc == 1) gotreply = 1; } return (gotreply); } /* * * 4.2.4. NAME REFRESH REQUEST * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |0| 0x8 |0|0|0|0|0 0|B| 0x0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0001 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / QUESTION_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0006 | NB_FLAGS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_ADDRESS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ /*ARGSUSED*/ static int smb_send_name_refresh_request(int bcast, struct name_question *question, struct resource_record *additional, int force) { int rc = 0; int gotreply = 0; uint32_t retries; uint32_t timeout; struct addr_entry *addr; struct addr_entry *destination; struct name_packet packet; unsigned char type; int i, addr_num, q_addrs = 0; type = question->name->name[15]; if ((type != 0x00) && (type != 0x20)) { syslog(LOG_ERR, "attempt to refresh non-local name"); smb_netbios_name_logf(question->name); question->name->attributes &= ~NAME_ATTR_LOCAL; return (-1); } switch (bcast) { case BROADCAST : if (bcast_num == 0) return (-1); destination = smb_bcast_list; addr_num = bcast_num; retries = BCAST_REQ_RETRY_COUNT; timeout = BCAST_REQ_RETRY_TIMEOUT; packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_BROADCAST; break; case UNICAST : if (nbns_num == 0) return (-1); destination = smb_nbns; addr_num = nbns_num; retries = UCAST_REQ_RETRY_COUNT; timeout = UCAST_REQ_RETRY_TIMEOUT; packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_UNICAST; break; default: destination = &question->name->addr_list; /* * the value of addr_num is irrelvant here, because * the code is going to do special_process so it doesn't * need the addr_num. We set a value here just to avoid * compiler warning. */ addr_num = 0; retries = UCAST_REQ_RETRY_COUNT; timeout = UCAST_REQ_RETRY_TIMEOUT; packet.info = NAME_REFRESH_REQUEST | NM_FLAGS_UNICAST; q_addrs = 1; break; } if (IS_UNIQUE(question->name->attributes) && (is_multihome((char *)(question->name->name)))) packet.info |= NAME_MULTIHOME_REGISTRATION_REQUEST; packet.qdcount = 1; /* question entries */ packet.question = question; packet.ancount = 0; /* answer recs */ packet.answer = NULL; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 1; /* additional recs */ packet.additional = additional; if (q_addrs) goto special_process; for (i = 0; i < addr_num; i++) { rc = smb_netbios_send_rcv(bcast, &destination[i], &packet, retries, timeout); if (rc == 1) gotreply = 1; } return (gotreply); special_process: addr = destination; do { rc = smb_netbios_send_rcv(bcast, addr, &packet, retries, timeout); if (rc == 1) gotreply = 1; addr = addr->forw; } while (addr != destination); return (gotreply); } /* * 4.2.5. POSITIVE NAME REGISTRATION RESPONSE * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| 0x0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0006 | NB_FLAGS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_ADDRESS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * * * 4.2.6. NEGATIVE NAME REGISTRATION RESPONSE * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |1| 0x5 |1|0|1|1|0 0|0| RCODE | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0006 | NB_FLAGS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_ADDRESS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * RCODE field values: * * Symbol Value Description: * * FMT_ERR 0x1 Format Error. Request was invalidly * formatted. * SRV_ERR 0x2 Server failure. Problem with NAME, cannot * process name. * IMP_ERR 0x4 Unsupported request error. Allowable only * for challenging NAME when gets an Update type * registration request. * RFS_ERR 0x5 Refused error. For policy reasons server * will not register this name from this host. * ACT_ERR 0x6 Active error. Name is owned by another node. * CFT_ERR 0x7 Name in conflict error. A UNIQUE name is * owned by more than one node. */ static int smb_send_name_registration_response(struct addr_entry *addr, struct name_packet *original_packet, unsigned short rcode) { struct name_packet packet; struct resource_record answer; bzero(&packet, sizeof (struct name_packet)); bzero(&answer, sizeof (struct resource_record)); packet.name_trn_id = original_packet->name_trn_id; packet.info = NAME_REGISTRATION_RESPONSE | NAME_NM_FLAGS_RA | (rcode & NAME_RCODE_MASK); packet.qdcount = 0; /* question entries */ packet.question = NULL; packet.ancount = 1; /* answer recs */ packet.answer = &answer; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 0; /* additional recs */ packet.additional = NULL; answer.name = original_packet->question->name; answer.rr_type = NAME_QUESTION_TYPE_NB; answer.rr_class = NAME_QUESTION_CLASS_IN; answer.ttl = original_packet->additional->ttl; answer.rdlength = original_packet->additional->rdlength; answer.rdata = original_packet->additional->rdata; return (smb_send_name_service_packet(addr, &packet)); } /* * 4.2.9. NAME RELEASE REQUEST & DEMAND * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |0| 0x6 |0|0|0|0|0 0|B| 0x0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0001 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / QUESTION_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x00000000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0006 | NB_FLAGS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_ADDRESS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * Since the RR_NAME is the same name as the QUESTION_NAME, the * RR_NAME representation must use label string pointers to the * QUESTION_NAME labels to guarantee the length of the datagram is * less than the maximum 576 bytes. This is the same condition as * with the NAME REGISTRATION REQUEST. */ static int smb_send_name_release_request_and_demand(int bcast, struct name_question *question, struct resource_record *additional) { int gotreply = 0; int i, rc; int addr_num; uint32_t retries; uint32_t timeout; struct addr_entry *destination; struct name_packet packet; if (bcast == BROADCAST) { if (bcast_num == 0) return (-1); destination = smb_bcast_list; addr_num = bcast_num; retries = 1; /* BCAST_REQ_RETRY_COUNT */ timeout = 100; /* BCAST_REQ_RETRY_TIMEOUT */ packet.info = NAME_RELEASE_REQUEST | NM_FLAGS_BROADCAST; } else { if (nbns_num == 0) return (-1); destination = smb_nbns; addr_num = nbns_num; retries = 1; /* UCAST_REQ_RETRY_COUNT */ timeout = 100; /* UCAST_REQ_RETRY_TIMEOUT */ packet.info = NAME_RELEASE_REQUEST | NM_FLAGS_UNICAST; } packet.qdcount = 1; /* question entries */ packet.question = question; packet.ancount = 0; /* answer recs */ packet.answer = NULL; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 1; /* additional recs */ packet.additional = additional; for (i = 0; i < addr_num; i++) { rc = smb_netbios_send_rcv(bcast, &destination[i], &packet, retries, timeout); if (rc == 1) gotreply = 1; } return (gotreply); } /* * 4.2.10. POSITIVE NAME RELEASE RESPONSE * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| 0x0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0006 | NB_FLAGS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_ADDRESS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * * 4.2.11. NEGATIVE NAME RELEASE RESPONSE * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |1| 0x6 |1|0|0|0|0 0|0| RCODE | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0006 | NB_FLAGS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_ADDRESS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * RCODE field values: * * Symbol Value Description: * * FMT_ERR 0x1 Format Error. Request was invalidly * formatted. * * SRV_ERR 0x2 Server failure. Problem with NAME, cannot * process name. * * RFS_ERR 0x5 Refused error. For policy reasons server * will not release this name from this host. * * ACT_ERR 0x6 Active error. Name is owned by another node. * Only that node may release it. A NetBIOS * Name Server can optionally allow a node to * release a name it does not own. This would * facilitate detection of inactive names for * nodes that went down silently. */ static int /* LINTED - E_STATIC_UNUSED */ smb_send_name_release_response(struct addr_entry *addr, struct name_packet *original_packet, unsigned short rcode) { struct name_packet packet; struct resource_record answer; bzero(&packet, sizeof (struct name_packet)); bzero(&answer, sizeof (struct resource_record)); packet.name_trn_id = original_packet->name_trn_id; packet.info = NAME_RELEASE_RESPONSE | (rcode & NAME_RCODE_MASK); packet.qdcount = 0; /* question entries */ packet.question = NULL; packet.ancount = 1; /* answer recs */ packet.answer = &answer; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 0; /* additional recs */ packet.additional = NULL; answer.name = original_packet->question->name; answer.rr_type = NAME_QUESTION_TYPE_NB; answer.rr_class = NAME_QUESTION_CLASS_IN; answer.ttl = original_packet->additional->ttl; answer.rdlength = original_packet->additional->rdlength; answer.rdata = original_packet->additional->rdata; return (smb_send_name_service_packet(addr, &packet)); } /* * * 4.2.12. NAME QUERY REQUEST * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |0| 0x0 |0|0|1|0|0 0|B| 0x0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0001 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / QUESTION_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ static int smb_send_name_query_request(int bcast, struct name_question *question) { int rc = 0; uint32_t retry, retries; uint32_t timeout; unsigned short tid; struct addr_entry *destination; struct name_packet packet; int i, addr_num; struct timespec st; if (bcast == BROADCAST) { if (bcast_num == 0) return (-1); destination = smb_bcast_list; addr_num = bcast_num; retries = BCAST_REQ_RETRY_COUNT; timeout = BCAST_REQ_RETRY_TIMEOUT; packet.info = NAME_QUERY_REQUEST | NM_FLAGS_BROADCAST; } else { if (nbns_num == 0) return (-1); destination = smb_nbns; addr_num = nbns_num; retries = UCAST_REQ_RETRY_COUNT; timeout = UCAST_REQ_RETRY_TIMEOUT; packet.info = NAME_QUERY_REQUEST | NM_FLAGS_UNICAST; } packet.qdcount = 1; /* question entries */ packet.question = question; packet.ancount = 0; /* answer recs */ packet.answer = NULL; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 0; /* additional recs */ packet.additional = NULL; for (i = 0; i < addr_num; i++) { for (retry = 0; retry < retries; retry++) { if ((destination->flags & ADDR_FLAG_VALID) == 0) break; tid = netbios_name_transcation_id++; packet.name_trn_id = tid; if (smb_send_name_service_packet(&destination[i], &packet) >= 0) { if ((rc = smb_netbios_process_response(tid, &destination[i], &packet, timeout)) != 0) break; } st.tv_sec = 0; st.tv_nsec = (timeout * 1000000); (void) nanosleep(&st, 0); } } return (rc); } /* * * 4.2.13. POSITIVE NAME QUERY RESPONSE * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |1| 0x0 |1|T|1|?|0 0|0| 0x0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB (0x0020) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | TTL | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | RDLENGTH | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | * | | * / ADDR_ENTRY ARRAY / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The ADDR_ENTRY ARRAY a sequence of zero or more ADDR_ENTRY * records. Each ADDR_ENTRY record represents an owner of a name. * For group names there may be multiple entries. However, the list * may be incomplete due to packet size limitations. Bit 22, "T", * will be set to indicate truncated data. * * Each ADDR_ENTRY has the following format: * * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_FLAGS | NB_ADDRESS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NB_ADDRESS (continued) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * * * 4.2.14. NEGATIVE NAME QUERY RESPONSE * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |1| 0x0 |1|0|1|?|0 0|0| RCODE | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * / / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NULL (0x000A) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x00000000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * RCODE field values: * * Symbol Value Description * * FMT_ERR 0x1 Format Error. Request was invalidly * formatted. * SRV_ERR 0x2 Server failure. Problem with NAME, cannot * process name. * NAM_ERR 0x3 Name Error. The name requested does not * exist. * IMP_ERR 0x4 Unsupported request error. Allowable only * for challenging NAME when gets an Update type * registration request. * RFS_ERR 0x5 Refused error. For policy reasons server * will not register this name from this host. */ static int smb_send_name_query_response(struct addr_entry *addr, struct name_packet *original_packet, struct name_entry *entry, unsigned short rcode) { struct addr_entry *raddr; struct name_packet packet; struct resource_record answer; unsigned short attr; unsigned char data[MAX_DATAGRAM_LENGTH]; unsigned char *scan = data; packet.name_trn_id = original_packet->name_trn_id; packet.info = NAME_QUERY_RESPONSE | (rcode & NAME_RCODE_MASK); packet.qdcount = 0; /* question entries */ packet.question = NULL; packet.ancount = 1; /* answer recs */ packet.answer = &answer; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 0; /* additional recs */ packet.additional = NULL; answer.name = entry; answer.rr_class = NAME_QUESTION_CLASS_IN; answer.ttl = entry->addr_list.ttl; answer.rdata = data; if (rcode) { answer.rr_type = NAME_RR_TYPE_NULL; answer.rdlength = 0; bzero(data, 6); } else { answer.rdlength = 0; answer.rr_type = NAME_QUESTION_TYPE_NB; raddr = &entry->addr_list; scan = data; do { attr = entry->attributes & (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); BE_OUT16(scan, attr); scan += 2; *scan++ = raddr->sin.sin_addr.s_addr; *scan++ = raddr->sin.sin_addr.s_addr >> 8; *scan++ = raddr->sin.sin_addr.s_addr >> 16; *scan++ = raddr->sin.sin_addr.s_addr >> 24; answer.rdlength += 6; raddr = raddr->forw; } while (raddr != &entry->addr_list); } return (smb_send_name_service_packet(addr, &packet)); } /* * 4.2.18. NODE STATUS RESPONSE * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_TRN_ID |1| 0x0 |1|0|0|0|0 0|0| 0x0 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0001 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x0000 | 0x0000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * / RR_NAME / * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NBSTAT (0x0021) | IN (0x0001) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | 0x00000000 | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | RDLENGTH | NUM_NAMES | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | | * + + * / NODE_NAME ARRAY / * + + * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * + + * / STATISTICS / * + + * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The NODE_NAME ARRAY is an array of zero or more NUM_NAMES entries * of NODE_NAME records. Each NODE_NAME entry represents an active * name in the same NetBIOS scope as the requesting name in the * local name table of the responder. RR_NAME is the requesting * name. * * NODE_NAME Entry: * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | | * +--- ---+ * | | * +--- NETBIOS FORMAT NAME ---+ * | | * +--- ---+ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NAME_FLAGS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * The NAME_FLAGS field: * * 1 1 1 1 1 1 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * | G | ONT |DRG|CNF|ACT|PRM| RESERVED | * +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ * * The NAME_FLAGS field is defined as: * * Symbol Bit(s) Description: * * RESERVED 7-15 Reserved for future use. Must be zero (0). * PRM 6 Permanent Name Flag. If one (1) then entry * is for the permanent node name. Flag is zero * (0) for all other names. * ACT 5 Active Name Flag. All entries have this flag * set to one (1). * CNF 4 Conflict Flag. If one (1) then name on this * node is in conflict. * DRG 3 Deregister Flag. If one (1) then this name * is in the process of being deleted. * ONT 1,2 Owner Node Type: * 00 = B node * 01 = P node * 10 = M node * 11 = Reserved for future use * G 0 Group Name Flag. * If one (1) then the name is a GROUP NetBIOS * name. * If zero (0) then it is a UNIQUE NetBIOS name. */ #define NAME_FLAGS_PERMANENT_NAME 0x0200 #define NAME_FLAGS_ACTIVE_NAME 0x0400 #define NAME_FLAGS_CONFLICT 0x0800 #define NAME_FLAGS_DEREGISTER 0x1000 #define NAME_FLAGS_ONT_MASK 0x6000 #define NAME_FLAGS_ONT_B_NODE 0x0000 #define NAME_FLAGS_ONT_P_NODE 0x2000 #define NAME_FLAGS_ONT_M_NODE 0x4000 #define NAME_FLAGS_ONT_RESERVED 0x6000 #define NAME_FLAGS_GROUP_NAME 0x8000 /* * STATISTICS Field of the NODE STATUS RESPONSE: * * 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | UNIT_ID (Unique unit ID) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | UNIT_ID,continued | JUMPERS | TEST_RESULT | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | VERSION_NUMBER | PERIOD_OF_STATISTICS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NUMBER_OF_CRCs | NUMBER_ALIGNMENT_ERRORS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NUMBER_OF_COLLISIONS | NUMBER_SEND_ABORTS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NUMBER_GOOD_SENDS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NUMBER_GOOD_RECEIVES | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NUMBER_RETRANSMITS | NUMBER_NO_RESOURCE_CONDITIONS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | NUMBER_FREE_COMMAND_BLOCKS | TOTAL_NUMBER_COMMAND_BLOCKS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * |MAX_TOTAL_NUMBER_COMMAND_BLOCKS| NUMBER_PENDING_SESSIONS | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | MAX_NUMBER_PENDING_SESSIONS | MAX_TOTAL_SESSIONS_POSSIBLE | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | SESSION_DATA_PACKET_SIZE | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ #define MAX_NETBIOS_REPLY_DATA_SIZE 500 static int smb_send_node_status_response(struct addr_entry *addr, struct name_packet *original_packet) { uint32_t net_ipaddr; int64_t max_connections; struct arpreq arpreq; struct name_packet packet; struct resource_record answer; unsigned char *scan; unsigned char *scan_end; unsigned char data[MAX_NETBIOS_REPLY_DATA_SIZE]; boolean_t scan_done = B_FALSE; bzero(&packet, sizeof (struct name_packet)); bzero(&answer, sizeof (struct resource_record)); packet.name_trn_id = original_packet->name_trn_id; packet.info = NODE_STATUS_RESPONSE; packet.qdcount = 0; /* question entries */ packet.question = NULL; packet.ancount = 1; /* answer recs */ packet.answer = &answer; packet.nscount = 0; /* authority recs */ packet.authority = NULL; packet.arcount = 0; /* additional recs */ packet.additional = NULL; answer.name = original_packet->question->name; answer.rr_type = NAME_RR_TYPE_NBSTAT; answer.rr_class = NAME_QUESTION_CLASS_IN; answer.ttl = 0; answer.rdata = data; scan = smb_netbios_cache_status(data, MAX_NETBIOS_REPLY_DATA_SIZE, original_packet->question->name->scope); scan_end = data + MAX_NETBIOS_REPLY_DATA_SIZE; if (smb_nic_exists(addr->sin.sin_addr.s_addr, B_TRUE)) net_ipaddr = addr->sin.sin_addr.s_addr; else net_ipaddr = 0; (void) smb_config_getnum(SMB_CI_MAX_CONNECTIONS, &max_connections); while (!scan_done) { if ((scan + 6) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } if (net_ipaddr != 0) { struct sockaddr_in *s_in; int s; s = socket(AF_INET, SOCK_DGRAM, 0); /* LINTED - E_BAD_PTR_CAST_ALIGN */ s_in = (struct sockaddr_in *)&arpreq.arp_pa; s_in->sin_family = AF_INET; s_in->sin_addr.s_addr = net_ipaddr; if (ioctl(s, SIOCGARP, (caddr_t)&arpreq) < 0) { bzero(scan, 6); } else { bcopy(&arpreq.arp_ha.sa_data, scan, 6); } (void) close(s); } else { bzero(scan, 6); } scan += 6; if ((scan + 26) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } bzero(scan, 26); scan += 26; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, 0); scan += 2; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, 0); scan += 2; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, 0); scan += 2; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, 0); scan += 2; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, 0); scan += 2; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, 0); scan += 2; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, 0); scan += 2; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, max_connections); scan += 2; if ((scan + 2) >= scan_end) { packet.info |= NAME_NM_FLAGS_TC; break; } BE_OUT16(scan, 0); scan += 2; scan_done = B_TRUE; } answer.rdlength = scan - data; return (smb_send_name_service_packet(addr, &packet)); } /* * * 5.1. NAME SERVICE PROTOCOLS * * A REQUEST packet is always sent to the well known UDP port - * NAME_SERVICE_UDP_PORT. The destination address is normally * either the IP broadcast address or the address of the NAME - the * address of the NAME server it set up at initialization time. In * rare cases, a request packet will be sent to an end node, e.g. a * NAME QUERY REQUEST sent to "challenge" a node. * * A RESPONSE packet is always sent to the source UDP port and * source IP address of the request packet. * * A DEMAND packet must always be sent to the well known UDP port - * NAME_SERVICE_UDP_PORT. There is no restriction on the target IP * address. * * Terms used in this section: * * tid - Transaction ID. This is a value composed from * the requestor's IP address and a unique 16 bit * value generated by the originator of the * transaction. */ /* * * 5.1.1. B-NODE ACTIVITY * * 5.1.1.1. B-NODE ADD NAME * * PROCEDURE add_name(name) * * (* * * Host initiated processing for a B node * *) * BEGIN * * REPEAT * * (* build name service packet *) * * ONT = B_NODE; (* broadcast node *) * G = UNIQUE; (* unique name *) * TTL = 0; * * broadcast NAME REGISTRATION REQUEST packet; * * (* * * remote node(s) will send response packet * * if applicable * *) * pause(BCAST_REQ_RETRY_TIMEOUT); * * UNTIL response packet is received or * retransmit count has been exceeded * * IF no response packet was received THEN * BEGIN (* no response *) * (* * * Build packet * *) * * ONT = B_NODE; (* broadcast node *) * G = UNIQUE; (* unique name *) * TTL = 0; * * (* * * Let other nodes known you have the name * *) * * broadcast NAME UPDATE REQUEST packet; * (* name can be added to local name table *) * return success; * END (* no response *) * ELSE * BEGIN (* got response *) * * (* * * Match return transaction id * * against tid sent in request * *) * * IF NOT response tid = request tid THEN * BEGIN * ignore response packet; * END * ELSE * CASE packet type OF * * NEGATIVE NAME REGISTRATION RESPONSE: * * return failure; (* name cannot be added *) * * POSITIVE NAME REGISTRATION RESPONSE: * END-NODE CHALLENGE NAME REGISTRATION RESPONSE: * * (* * * B nodes should normally not get this * * response. * *) * * ignore packet; * END (* case *); * END (* got response *) * END (* procedure *) * * * * 5.1.1.2. B-NODE ADD_GROUP NAME * * PROCEDURE add_group_name(name) * * (* * * Host initiated processing for a B node * *) * * BEGIN * (* * * same as for a unique name with the * * exception that the group bit (G) must * * be set in the request packets. * *) * * ... * G = GROUP; * ... * ... * * (* * * broadcast request ... * *) * * * END */ static int smb_name_Bnode_add_name(struct name_entry *name) { struct name_question question; struct resource_record additional; unsigned char data[8]; unsigned short attr; struct addr_entry *addr; int rc = 0; addr = &name->addr_list; do { /* build name service packet */ question.name = name; /* * question.name->attributes |= NAME_NB_FLAGS_ONT_B; * This is commented because NAME_NB_FLAGS_ONT_B is 0 */ question.question_type = NAME_QUESTION_TYPE_NB; question.question_class = NAME_QUESTION_CLASS_IN; additional.name = name; additional.rr_class = NAME_QUESTION_CLASS_IN; additional.ttl = 0; additional.rdata = data; additional.rdlength = 6; additional.rr_type = NAME_QUESTION_TYPE_NB; attr = name->attributes & (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); BE_OUT16(&data[0], attr); (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr, sizeof (uint32_t)); rc |= smb_send_name_registration_request(BROADCAST, &question, &additional); addr = addr->forw; } while (addr != &name->addr_list); return (rc); } /* * 5.1.1.3. B-NODE FIND_NAME * * PROCEDURE find_name(name) * * (* * * Host initiated processing for a B node * *) * * BEGIN * * REPEAT * (* * * build packet * *) * ONT = B; * TTL = 0; * G = DONT CARE; * raddr = raddr->forw; * * broadcast NAME QUERY REQUEST packet; * (* * * a node might send response packet * *) * * pause(BCAST_REQ_RETRY_TIMEOUT); * UNTIL response packet received OR * max transmit threshold exceeded * * IF no response packet received THEN * return failure; * ELSE * IF NOT response tid = request tid THEN * ignore packet; * ELSE * CASE packet type OF * POSITIVE NAME QUERY RESPONSE: * (* * * Start a timer to detect conflict. * * * * Be prepared to detect conflict if * * any more response packets are received. * * * *) * * save response as authoritative response; * start_timer(CONFLICT_TIMER); * return success; * * NEGATIVE NAME QUERY RESPONSE: * REDIRECT NAME QUERY RESPONSE: * * (* * * B Node should normally not get either * * response. * *) * * ignore response packet; * * END (* case *) * END (* procedure *) */ static int smb_name_Bnode_find_name(struct name_entry *name) { struct name_question question; question.name = name; question.question_type = NAME_QUESTION_TYPE_NB; question.question_class = NAME_QUESTION_CLASS_IN; return (smb_send_name_query_request(BROADCAST, &question)); } /* * 5.1.1.4. B NODE NAME RELEASE * * PROCEDURE delete_name (name) * BEGIN * * REPEAT * * (* * * build packet * *) * ... * * (* * * send request * *) * * broadcast NAME RELEASE REQUEST packet; * * (* * * no response packet expected * *) * * pause(BCAST_REQ_RETRY_TIMEOUT); * * UNTIL retransmit count has been exceeded * END (* procedure *) */ static int smb_name_Bnode_delete_name(struct name_entry *name) { struct name_question question; struct resource_record additional; struct addr_entry *raddr; unsigned char data[MAX_DATAGRAM_LENGTH]; unsigned char *scan = data; uint32_t attr; /* build packet */ question.name = name; question.question_type = NAME_QUESTION_TYPE_NB; question.question_class = NAME_QUESTION_CLASS_IN; additional.name = name; additional.rr_class = NAME_QUESTION_CLASS_IN; additional.ttl = 0; additional.rdata = data; additional.rdlength = 0; additional.rr_type = NAME_QUESTION_TYPE_NB; raddr = &name->addr_list; scan = data; do { attr = name->attributes & (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); BE_OUT16(scan, attr); scan += 2; *scan++ = raddr->sin.sin_addr.s_addr; *scan++ = raddr->sin.sin_addr.s_addr >> 8; *scan++ = raddr->sin.sin_addr.s_addr >> 16; *scan++ = raddr->sin.sin_addr.s_addr >> 24; additional.rdlength += 6; } while (raddr != &name->addr_list); return (smb_send_name_release_request_and_demand(BROADCAST, &question, &additional)); } /* * * 5.1.2. P-NODE ACTIVITY * * All packets sent or received by P nodes are unicast UDP packets. * A P node sends name service requests to the NAME node that is * specified in the P-node configuration. * * 5.1.2.1. P-NODE ADD_NAME * * PROCEDURE add_name(name) * * (* * * Host initiated processing for a P node * *) * * BEGIN * * REPEAT * (* * * build packet * *) * * ONT = P; * G = UNIQUE; * ... * * (* * * send request * *) * * unicast NAME REGISTRATION REQUEST packet; * * (* * * NAME will send response packet * *) * * IF receive a WACK RESPONSE THEN * pause(time from TTL field of response); * ELSE * pause(UCAST_REQ_RETRY_TIMEOUT); * UNTIL response packet is received OR * retransmit count has been exceeded * * IF no response packet was received THEN * BEGIN (* no response *) * (* * * NAME is down. Cannot claim name. * *) * * return failure; (* name cannot be claimed *) * END (* no response *) * ELSE * BEGIN (* response *) * IF NOT response tid = request tid THEN * BEGIN * (* Packet may belong to another transaction *) * ignore response packet; * END * ELSE * CASE packet type OF * * POSITIVE NAME REGISTRATION RESPONSE: * * (* * * name can be added * *) * * adjust refresh timeout value, TTL, for this name; * return success; (* name can be added *) * * NEGATIVE NAME REGISTRATION RESPONSE: * return failure; (* name cannot be added *) * * END-NODE CHALLENGE REGISTRATION REQUEST: * BEGIN (* end node challenge *) * * (* * * The response packet has in it the * * address of the presumed owner of the * * name. Challenge that owner. * * If owner either does not * * respond or indicates that he no longer * * owns the name, claim the name. * * Otherwise, the name cannot be claimed. * * * *) * * REPEAT * (* * * build packet * *) * ... * * unicast NAME QUERY REQUEST packet to the * address contained in the END NODE * CHALLENGE RESPONSE packet; * * (* * * remote node may send response packet * *) * * pause(UCAST_REQ_RETRY_TIMEOUT); * * UNTIL response packet is received or * retransmit count has been exceeded * IF no response packet is received OR * NEGATIVE NAME QUERY RESPONSE packet * received THEN * BEGIN (* update *) * * (* * * name can be claimed * *) * * REPEAT * * (* * * build packet * *) * ... * * unicast NAME UPDATE REQUEST to NAME; * * (* * * NAME node will send response packet * *) * * IF receive a WACK RESPONSE THEN * pause(time from TTL field of response); * ELSE * pause(UCAST_REQ_RETRY_TIMEOUT); * UNTIL response packet is received or * retransmit count has been exceeded * IF no response packet received THEN * BEGIN (* no response *) * * (* * * name could not be claimed * *) * * return failure; * END (* no response *) * ELSE * CASE packet type OF * POSITIVE NAME REGISTRATION RESPONSE: * (* * * add name * *) * return success; * NEGATIVE NAME REGISTRATION RESPONSE: * * (* * * you lose ... * *) * return failure; * END (* case *) * END (* update *) * ELSE * * (* * * received a positive response to the "challenge" * * Remote node still has name * *) * * return failure; * END (* end node challenge *) * END (* response *) * END (* procedure *) * * * 5.1.2.2. P-NODE ADD GROUP NAME * * PROCEDURE add_group_name(name) * * (* * * Host initiated processing for a P node * *) * * BEGIN * (* * * same as for a unique name, except that the * * request packet must indicate that a * * group name claim is being made. * *) * * ... * G = GROUP; * ... * * (* * * send packet * *) * ... * * * END */ static int smb_name_Pnode_add_name(struct name_entry *name) { struct name_question question; struct resource_record additional; unsigned char data[8]; unsigned short attr; struct addr_entry *addr; int rc = 0; /* build packet */ addr = &name->addr_list; do { question.name = name; question.question_type = NAME_QUESTION_TYPE_NB; question.question_class = NAME_QUESTION_CLASS_IN; additional.name = name; additional.rr_class = NAME_QUESTION_CLASS_IN; additional.ttl = 0; additional.rdata = data; additional.rdlength = 6; additional.rr_type = NAME_QUESTION_TYPE_NB; attr = name->attributes & (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); BE_OUT16(&data[0], attr); (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr, sizeof (uint32_t)); rc |= smb_send_name_registration_request(UNICAST, &question, &additional); addr = addr->forw; } while (addr != &name->addr_list); return (rc); } static int smb_name_Pnode_refresh_name(struct name_entry *name) { struct name_question question; struct resource_record additional; unsigned char data[8]; unsigned short attr; struct addr_entry *addr; int rc = 0; /* build packet */ addr = &name->addr_list; do { question.name = name; question.question_type = NAME_QUESTION_TYPE_NB; question.question_class = NAME_QUESTION_CLASS_IN; additional.name = name; additional.rr_class = NAME_QUESTION_CLASS_IN; additional.ttl = 0; additional.rdata = data; additional.rdlength = 6; additional.rr_type = NAME_QUESTION_TYPE_NB; attr = name->attributes & (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); BE_OUT16(&data[0], attr); (void) memcpy(&data[2], &addr->sin.sin_addr.s_addr, sizeof (uint32_t)); rc |= smb_send_name_refresh_request(UNICAST, &question, &additional, 1); addr = addr->forw; } while (addr != &name->addr_list); return (rc); } /* * 5.1.2.3. P-NODE FIND NAME * * PROCEDURE find_name(name) * * (* * * Host initiated processing for a P node * *) * * BEGIN * REPEAT * (* * * build packet * *) * * ONT = P; * G = DONT CARE; * * unicast NAME QUERY REQUEST packet; * * (* * * a NAME node might send response packet * *) * * IF receive a WACK RESPONSE THEN * pause(time from TTL field of response); * ELSE * pause(UCAST_REQ_RETRY_TIMEOUT); * UNTIL response packet received OR * max transmit threshold exceeded * * IF no response packet received THEN * return failure; * ELSE * IF NOT response tid = request tid THEN * ignore packet; * ELSE * CASE packet type OF * POSITIVE NAME QUERY RESPONSE: * return success; * * REDIRECT NAME QUERY RESPONSE: * * (* * * NAME node wants this end node * * to use some other NAME node * * to resolve the query. * *) * * repeat query with NAME address * in the response packet; * NEGATIVE NAME QUERY RESPONSE: * return failure; * * END (* case *) * END (* procedure *) */ static int smb_name_Pnode_find_name(struct name_entry *name) { struct name_question question; /* * Host initiated processing for a P node */ question.name = name; question.name->attributes |= NAME_NB_FLAGS_ONT_P; question.question_type = NAME_QUESTION_TYPE_NB; question.question_class = NAME_QUESTION_CLASS_IN; return (smb_send_name_query_request(UNICAST, &question)); } /* * 5.1.2.4. P-NODE DELETE_NAME * * PROCEDURE delete_name (name) * * (* * * Host initiated processing for a P node * *) * * BEGIN * * REPEAT * * (* * * build packet * *) * ... * * (* * * send request * *) * * unicast NAME RELEASE REQUEST packet; * IF receive a WACK RESPONSE THEN * pause(time from TTL field of response); * ELSE * pause(UCAST_REQ_RETRY_TIMEOUT); * UNTIL retransmit count has been exceeded * or response been received * * IF response has been received THEN * CASE packet type OF * POSITIVE NAME RELEASE RESPONSE: * return success; * NEGATIVE NAME RELEASE RESPONSE: * * (* * * NAME does want node to delete this * * name !!! * *) * * return failure; * END (* case *) * END (* procedure *) */ static int smb_name_Pnode_delete_name(struct name_entry *name) { struct name_question question; struct resource_record additional; struct addr_entry *raddr; unsigned char data[MAX_DATAGRAM_LENGTH]; unsigned char *scan = data; uint32_t attr; /* build packet */ question.name = name; question.name->attributes |= NAME_NB_FLAGS_ONT_P; question.question_type = NAME_QUESTION_TYPE_NB; question.question_class = NAME_QUESTION_CLASS_IN; additional.name = name; additional.rr_class = NAME_QUESTION_CLASS_IN; additional.ttl = 0; additional.rdata = data; additional.rdlength = 0; additional.rr_type = NAME_QUESTION_TYPE_NB; raddr = &name->addr_list; do { scan = data; attr = name->attributes & (NAME_ATTR_GROUP | NAME_ATTR_OWNER_NODE_TYPE); BE_OUT16(scan, attr); scan += 2; *scan++ = raddr->sin.sin_addr.s_addr; *scan++ = raddr->sin.sin_addr.s_addr >> 8; *scan++ = raddr->sin.sin_addr.s_addr >> 16; *scan++ = raddr->sin.sin_addr.s_addr >> 24; additional.rdlength = 6; raddr = raddr->forw; (void) smb_send_name_release_request_and_demand(UNICAST, &question, &additional); } while (raddr != &name->addr_list); return (1); } /* * 5.1.3. M-NODE ACTIVITY * * M nodes behavior is similar to that of P nodes with the addition * of some B node-like broadcast actions. M node name service * proceeds in two steps: * * 1.Use broadcast UDP based name service. Depending on the * operation, goto step 2. * * 2.Use directed UDP name service. * * The following code for M nodes is exactly the same as for a P * node, with the exception that broadcast operations are done * before P type operation is attempted. * * 5.1.3.1. M-NODE ADD NAME * * PROCEDURE add_name(name) * * (* * * Host initiated processing for a M node * *) * * BEGIN * * (* * * check if name exists on the * * broadcast area * *) * REPEAT * (* build packet *) * * .... * broadcast NAME REGISTRATION REQUEST packet; * pause(BCAST_REQ_RETRY_TIMEOUT); * * UNTIL response packet is received or * retransmit count has been exceeded * * IF valid response received THEN * BEGIN * (* cannot claim name *) * * return failure; * END * * (* * * No objections received within the * * broadcast area. * * Send request to name server. * *) * * REPEAT * (* * * build packet * *) * * ONT = M; * ... * * unicast NAME REGISTRATION REQUEST packet; * * (* * * remote NAME will send response packet * *) * * IF receive a WACK RESPONSE THEN * pause(time from TTL field of response); * ELSE * pause(UCAST_REQ_RETRY_TIMEOUT); * * UNTIL response packet is received or * retransmit count has been exceeded * * IF no response packet was received THEN * BEGIN (* no response *) * (* * * NAME is down. Cannot claim name. * *) * return failure; (* name cannot be claimed *) * END (* no response *) * ELSE * BEGIN (* response *) * IF NOT response tid = request tid THEN * BEGIN * ignore response packet; * END * ELSE * CASE packet type OF * POSITIVE NAME REGISTRATION RESPONSE: * * (* * * name can be added * *) * * adjust refresh timeout value, TTL; * return success; (* name can be added *) * * NEGATIVE NAME REGISTRATION RESPONSE: * return failure; (* name cannot be added *) * * END-NODE CHALLENGE REGISTRATION REQUEST: * BEGIN (* end node challenge *) * * (* * * The response packet has in it the * * address of the presumed owner of the * * name. Challenge that owner. * * If owner either does not * * respond or indicates that he no longer * * owns the name, claim the name. * * Otherwise, the name cannot be claimed. * * * *) * * REPEAT * (* * * build packet * *) * ... * * (* * * send packet to address contained in the * * response packet * *) * * unicast NAME QUERY REQUEST packet; * * (* * * remote node may send response packet * *) * * pause(UCAST_REQ_RETRY_TIMEOUT); * * UNTIL response packet is received or * retransmit count has been exceeded * IF no response packet is received THEN * BEGIN (* no response *) * * (* * * name can be claimed * *) * REPEAT * * (* * * build packet * *) * ... * * unicast NAME UPDATE REQUEST to NAME; * * (* * * NAME node will send response packet * *) * * IF receive a WACK RESPONSE THEN * pause(time from TTL field of response); * ELSE * pause(UCAST_REQ_RETRY_TIMEOUT); * * UNTIL response packet is received or * retransmit count has been exceeded * IF no response packet received THEN * BEGIN (* no response *) * * (* * * name could not be claimed * *) * * return failure; * END (* no response *) * ELSE * CASE packet type OF * POSITIVE NAME REGISTRATION RESPONSE: * (* * * add name * *) * * return success; * NEGATIVE NAME REGISTRATION RESPONSE: * (* * * you lose ... * *) * * return failure; * END (* case *) * END (* no response *) * ELSE * IF NOT response tid = request tid THEN * BEGIN * ignore response packet; * END * * (* * * received a response to the "challenge" * * packet * *) * * CASE packet type OF * POSITIVE NAME QUERY: * * (* * * remote node still has name. * *) * * return failure; * NEGATIVE NAME QUERY: * * (* * * remote node no longer has name * *) * * return success; * END (* case *) * END (* end node challenge *) * END (* case *) * END (* response *) * END (* procedure *) * * * 5.1.3.2. M-NODE ADD GROUP NAME * * PROCEDURE add_group_name(name) * * (* * * Host initiated processing for a P node * *) * * BEGIN * (* * * same as for a unique name, except that the * * request packet must indicate that a * * group name claim is being made. * *) * * ... * G = GROUP; * ... * * (* * * send packet * *) * ... * * * END */ static int smb_name_Mnode_add_name(struct name_entry *name) { if (smb_name_Bnode_add_name(name) > 0) { if (nbns_num == 0) return (1); /* No name server configured */ return (smb_name_Pnode_add_name(name)); } return (-1); } static int smb_name_Hnode_add_name(struct name_entry *name) { if (nbns_num > 0) { if (smb_name_Pnode_add_name(name) == 1) return (1); } return (smb_name_Bnode_add_name(name)); } /* * 5.1.3.3. M-NODE FIND NAME * * PROCEDURE find_name(name) * * (* * * Host initiated processing for a M node * *) * * BEGIN * (* * * check if any node on the broadcast * * area has the name * *) * * REPEAT * (* build packet *) * ... * * broadcast NAME QUERY REQUEST packet; * pause(BCAST_REQ_RETRY_TIMEOUT); * UNTIL response packet received OR * max transmit threshold exceeded * * IF valid response received THEN * BEGIN * save response as authoritative response; * start_timer(CONFLICT_TIMER); * return success; * END * * (* * * no valid response on the b'cast segment. * * Try the name server. * *) * * REPEAT * (* * * build packet * *) * * ONT = M; * G = DONT CARE; * * unicast NAME QUERY REQUEST packet to NAME; * * (* * * a NAME node might send response packet * *) * * IF receive a WACK RESPONSE THEN * pause(time from TTL field of response); * ELSE * pause(UCAST_REQ_RETRY_TIMEOUT); * UNTIL response packet received OR * max transmit threshold exceeded * * IF no response packet received THEN * return failure; * ELSE * IF NOT response tid = request tid THEN * ignore packet; * ELSE * CASE packet type OF * POSITIVE NAME QUERY RESPONSE: * return success; * * REDIRECT NAME QUERY RESPONSE: * * (* * * NAME node wants this end node * * to use some other NAME node * * to resolve the query. * *) * * repeat query with NAME address * in the response packet; * NEGATIVE NAME QUERY RESPONSE: * return failure; * * END (* case *) * END (* procedure *) */ static int smb_name_Mnode_find_name(struct name_entry *name) { if (smb_name_Bnode_find_name(name) == 1) return (1); if (nbns_num == 0) return (1); /* No name server configured */ return (smb_name_Pnode_find_name(name)); } static int smb_name_Hnode_find_name(struct name_entry *name) { if (nbns_num > 0) if (smb_name_Pnode_find_name(name) == 1) return (1); return (smb_name_Bnode_find_name(name)); } /* * 5.1.3.4. M-NODE DELETE NAME * * PROCEDURE delete_name (name) * * (* * * Host initiated processing for a P node * *) * * BEGIN * (* * * First, delete name on NAME * *) * * REPEAT * * (* * * build packet * struct addr_entry *addr; * *) * ... * * (* * * send request * *) * * unicast NAME RELEASE REQUEST packet to NAME; * * IF receive a WACK RESPONSE THEN * pause(time from TTL field of response); * ELSE * pause(UCAST_REQ_RETRY_TIMEOUT); * UNTIL retransmit count has been exceeded * or response been received * * IF response has been received THEN * CASE packet type OF * POSITIVE NAME RELEASE RESPONSE: * (* * * Deletion of name on b'cast segment is deferred * * until after NAME has deleted the name * *) * * REPEAT * (* build packet *) * * ... * broadcast NAME RELEASE REQUEST; * pause(BCAST_REQ_RETRY_TIMEOUT); * UNTIL rexmt threshold exceeded * * return success; * NEGATIVE NAME RELEASE RESPONSE: * * (* * * NAME does want node to delete this * * name * *) * return failure; * END (* case *) * END (* procedure *) */ static int smb_name_Mnode_delete_name(struct name_entry *name) { (void) smb_name_Bnode_delete_name(name); if (nbns_num == 0) return (-1); /* No name server configured */ if (smb_name_Pnode_delete_name(name) > 0) return (1); return (-1); } static int smb_name_Hnode_delete_name(struct name_entry *name) { if (nbns_num > 0) if (smb_name_Pnode_delete_name(name) > 0) return (1); return (smb_name_Bnode_delete_name(name)); } /* * 5.1.1.5. B-NODE INCOMING PACKET PROCESSING * * Following processing is done when broadcast or unicast packets * are received at the NAME_SERVICE_UDP_PORT. * * PROCEDURE process_incoming_packet(packet) * * (* * * Processing initiated by incoming packets for a B node * *) * * BEGIN * (* * * Note: response packets are always sent * * to: * * source IP address of request packet * * source UDP port of request packet * *) * * CASE packet type OF * * NAME REGISTRATION REQUEST (UNIQUE): * IF name exists in local name table THEN * send NEGATIVE_NAME_REGISTRATION_RESPONSE ; * NAME REGISTRATION REQUEST (GROUP): * IF name exists in local name table THEN * BEGIN * IF local entry is a unique name THEN * send NEGATIVE_NAME_REGISTRATION_RESPONSE ; * END * NAME QUERY REQUEST: * IF name exists in local name table THEN * BEGIN * build response packet; * send POSITIVE_NAME_QUERY_RESPONSE; * POSITIVE NAME QUERY RESPONSE: * IF name conflict timer is not active THEN * BEGIN * (* * * timer has expired already... ignore this * * packet * *) * * return; * END * ELSE (* timer is active *) * IF a response for this name has previously been * received THEN * BEGIN (* existing entry *) * * (* * * we sent out a request packet, and * * have already received (at least) * * one response * * * * Check if conflict exists. * * If so, send out a conflict packet. * * * * Note: detecting conflict does NOT * * affect any existing sessions. * * * *) * * (* * * Check for name conflict. * * See "Name Conflict" in Concepts and Methods * *) * check saved authoritative response against * information in this response packet; * IF conflict detected THEN * BEGIN * unicast NAME CONFLICT DEMAND packet; * IF entry exists in cache THEN * BEGIN * remove entry from cache; * END * END * END (* existing entry *) * ELSE * BEGIN * (* * * Note: If this was the first response * * to a name query, it would have been * * handled in the * * find_name() procedure. * *) * * ignore packet; * END * NAME CONFLICT DEMAND: * IF name exists in local name table THEN * BEGIN * mark name as conflict detected; * * (* * * a name in the state "conflict detected" * * does not "logically" exist on that node. * * No further session will be accepted on * * that name. * * No datagrams can be sent against that name. * * Such an entry will not be used for * * purposes of processing incoming request * * packets. * * The only valid user NetBIOS operation * * against such a name is DELETE NAME. * *) * END * NAME RELEASE REQUEST: * IF caching is being done THEN * BEGIN * remove entry from cache; * END * NAME UPDATE REQUEST: * IF caching is being done THEN * BEGIN * IF entry exists in cache already, * update cache; * ELSE IF name is "interesting" THEN * BEGIN * add entry to cache; * END * END * * NODE STATUS REQUEST: * IF name exists in local name table THEN * BEGIN * (* * * send only those names that are * * in the same scope as the scope * * field in the request packet * *) * * send NODE STATUS RESPONSE; * END * END */ static void smb_name_process_Bnode_packet(struct name_packet *packet, struct addr_entry *addr) { struct name_entry *name; struct name_entry *entry; struct name_question *question; struct resource_record *additional; question = packet->question; additional = packet->additional; switch (packet->info & NAME_OPCODE_OPCODE_MASK) { case NAME_OPCODE_REFRESH: /* Guard against malformed packets */ if ((question == 0) || (additional == 0)) break; if (additional->name->addr_list.sin.sin_addr.s_addr == 0) break; name = question->name; name->addr_list.ttl = additional->ttl; name->attributes = additional->name->attributes; name->addr_list.sin = additional->name->addr_list.sin; name->addr_list.forw = name->addr_list.back = &name->addr_list; if ((entry = smb_netbios_cache_lookup_addr(name)) != 0) { smb_netbios_cache_update_entry(entry, question->name); smb_netbios_cache_unlock_entry(entry); } else (void) smb_netbios_cache_insert(question->name); break; case NAME_OPCODE_QUERY: /* * This opcode covers both NAME_QUERY_REQUEST and * NODE_STATUS_REQUEST. They can be distinguished * based on the type of question entry. */ /* All query requests have to have question entry */ if (question == 0) break; if (question->question_type == NAME_QUESTION_TYPE_NB) { name = question->name; if ((entry = smb_netbios_cache_lookup(name)) != 0) { (void) smb_send_name_query_response(addr, packet, entry, 0); smb_netbios_cache_unlock_entry(entry); } } else if (question->question_type == NAME_QUESTION_TYPE_NBSTAT) { /* * Name of "*" may be used to force node to * divulge status for administrative purposes */ name = question->name; entry = 0; if (NETBIOS_NAME_IS_STAR(name->name) || ((entry = smb_netbios_cache_lookup(name)) != 0)) { if (entry) smb_netbios_cache_unlock_entry(entry); /* * send only those names that are * in the same scope as the scope * field in the request packet */ (void) smb_send_node_status_response(addr, packet); } } break; default: break; } } /* * 5.1.2.5. P-NODE INCOMING PACKET PROCESSING * * Processing initiated by reception of packets at a P node * * PROCEDURE process_incoming_packet(packet) * * (* * * Processing initiated by incoming packets at a P node * *) * * BEGIN * (* * * always ignore UDP broadcast packets * *) * * IF packet was sent as a broadcast THEN * BEGIN * ignore packet; * return; * END * CASE packet type of * * NAME CONFLICT DEMAND: * IF name exists in local name table THEN * mark name as in conflict; * return; * * NAME QUERY REQUEST: * IF name exists in local name table THEN * BEGIN (* name exists *) * * (* * * build packet * *) * ... * * (* * * send response to the IP address and port * * number from which the request was received. * *) * * send POSITIVE_NAME_QUERY_RESPONSE ; * return; * END (* exists *) * ELSE * BEGIN (* does not exist *) * * (* * * send response to the requestor * *) * * send NEGATIVE_NAME_QUERY_RESPONSE ; * return; * END (* does not exist *) * NODE STATUS REQUEST: * (* * * Name of "*" may be used for force node to * * divulge status for administrative purposes * *) * IF name in local name table OR name = "*" THEN * BEGIN * (* * * Build response packet and * * send to requestor node * * Send only those names that are * * in the same scope as the scope * * in the request packet. * *) * * send NODE_STATUS_RESPONSE; * END * * NAME RELEASE REQUEST: * (* * * This will be received if the NAME wants to flush the * * name from the local name table, or from the local * * cache. * *) * * IF name exists in the local name table THEN * BEGIN * delete name from local name table; * inform user that name has been deleted; * END * END (* case *) * END (* procedure *) * * (* * * Incoming packet processing on a NS node * *) * * BEGIN * IF packet was sent as a broadcast THEN * BEGIN * discard packet; * return; * END * CASE packet type of * * NAME REGISTRATION REQUEST (UNIQUE): * IF unique name exists in data base THEN * BEGIN (* unique name exists *) * (* * * NAME node may be a "passive" * * server in that it expects the * * end node to do the challenge * * server. Such a NAME node is * * called a "non-secure" server. * * A "secure" server will do the * * challenging before it sends * * back a response packet. * *) * * IF non-secure THEN * BEGIN * (* * * build response packet * *) * ... * * * (* * * let end node do the challenge * *) * * send END-NODE CHALLENGE NAME REGISTRATION * RESPONSE; * return; * END * ELSE * (* * * secure server - do the name * * challenge operation * *) * * REPEAT * send NAME QUERY REQUEST; * pause(UCAST_REQ_RETRY_TIMEOUT); * UNTIL response has been received or * retransmit count has been exceeded * IF no response was received THEN * BEGIN * * (* node down *) * * update data base - remove entry; * update data base - add new entry; * send POSITIVE NAME REGISTRATION RESPONSE; * return; * END * ELSE * BEGIN (* challenged node replied *) * (* * * challenged node replied with * * a response packet * *) * * CASE packet type * * POSITIVE NAME QUERY RESPONSE: * * (* * * name still owned by the * * challenged node * * * * build packet and send response * *) * ... * * * (* * * Note: The NAME will need to * * keep track (based on transaction id) of * * the IP address and port number * * of the original requestor. * *) * * send NEGATIVE NAME REGISTRATION RESPONSE; * return; * NEGATIVE NAME QUERY RESPONSE: * * update data base - remove entry; * update data base - add new entry; * * (* * * build response packet and send * * response * *) * send POSITIVE NAME REGISTRATION RESPONSE; * return; * END (* case *) * END (* challenged node replied *) * END (* unique name exists in data base *) * ELSE * IF group name exists in data base THEN * BEGIN (* group names exists *) * * (* * * Members of a group name are NOT * * challenged. * * Make the assumption that * * at least some of the group members * * are still alive. * * Refresh mechanism will * * allow the NAME to detect when all * * members of a group no longer use that * * name * *) * * send NEGATIVE NAME REGISTRATION RESPONSE; * END (* group name exists *) * ELSE * BEGIN (* name does not exist *) * * (* * * Name does not exist in data base * * * * This code applies to both non-secure * * and secure server. * *) * * update data base - add new entry; * send POSITIVE NAME REGISTRATION RESPONSE; * return; * END * * NAME QUERY REQUEST: * IF name exists in data base THEN * BEGIN * (* * * build response packet and send to * * requestor * *) * ... * * send POSITIVE NAME QUERY RESPONSE; * return; * ELSE * BEGIN * (* * * build response packet and send to * * requestor * *) * ... * * send NEGATIVE NAME QUERY RESPONSE; * return; * END * * NAME REGISTRATION REQUEST (GROUP): * IF name exists in data base THEN * BEGIN * IF local entry is a unique name THEN * BEGIN (* local is unique *) * * IF non-secure THEN * BEGIN * send END-NODE CHALLENGE NAME * REGISTRATION RESPONSE; * return; * END * * REPEAT * send NAME QUERY REQUEST; * pause(UCAST_REQ_RETRY_TIMEOUT); * UNTIL response received or * retransmit count exceeded * IF no response received or * NEGATIVE NAME QUERY RESPONSE * received THEN * BEGIN * update data base - remove entry; * update data base - add new entry; * send POSITIVE NAME REGISTRATION RESPONSE; * return; * END * ELSE * BEGIN * (* * * name still being held * * by challenged node * *) * * send NEGATIVE NAME REGISTRATION RESPONSE; * END * END (* local is unique *) * ELSE * BEGIN (* local is group *) * (* * * existing entry is a group name * *) * * update data base - remove entry; * update data base - add new entry; * send POSITIVE NAME REGISTRATION RESPONSE; * return; * END (* local is group *) * END (* names exists *) * ELSE * BEGIN (* does not exist *) * * (* name does not exist in data base *) * * update data base - add new entry; * send POSITIVE NAME REGISTRATION RESPONSE; * return; * END (* does not exist *) * * NAME RELEASE REQUEST: * * (* * * secure server may choose to disallow * * a node from deleting a name * *) * * update data base - remove entry; * send POSITIVE NAME RELEASE RESPONSE; * return; * * NAME UPDATE REQUEST: * * (* * * End-node completed a successful challenge, * * no update database * *) * * IF secure server THEN * send NEGATIVE NAME REGISTRATION RESPONSE; * ELSE * BEGIN (* new entry *) * IF entry already exists THEN * update data base - remove entry; * update data base - add new entry; * send POSITIVE NAME REGISTRATION RESPONSE; * start_timer(TTL); * END * * NAME REFRESH REQUEST: * check for consistency; * * IF node not allowed to have name THEN * BEGIN * * (* * * tell end node that it can't have name * *) * send NEGATIVE NAME REGISTRATION RESPONSE; * END * ELSE * BEGIN * * (* * * send confirmation response to the * * end node. * *) * send POSITIVE NAME REGISTRATION; * start_timer(TTL); * END * return; * END (* case *) * END (* procedure *) */ static void smb_name_process_Pnode_packet(struct name_packet *packet, struct addr_entry *addr) { struct name_entry *name; struct name_entry *entry; struct name_question *question; struct resource_record *additional; question = packet->question; additional = packet->additional; if (packet->info & NAME_NM_FLAGS_B) { /* * always ignore UDP broadcast packets */ return; } switch (packet->info & NAME_OPCODE_OPCODE_MASK) { case NAME_OPCODE_REFRESH: /* Guard against malformed packets */ if ((question == 0) || (additional == 0)) break; if (additional->name->addr_list.sin.sin_addr.s_addr == 0) break; name = question->name; name->addr_list.ttl = additional->ttl; name->attributes = additional->name->attributes; name->addr_list.sin = additional->name->addr_list.sin; name->addr_list.forw = name->addr_list.back = &name->addr_list; if ((entry = smb_netbios_cache_lookup(name)) != 0) { smb_netbios_cache_update_entry(entry, name); smb_netbios_cache_unlock_entry(entry); } else (void) smb_netbios_cache_insert(name); (void) smb_send_name_registration_response(addr, packet, 0); break; case NAME_OPCODE_QUERY: /* * This opcode covers both NAME_QUERY_REQUEST and * NODE_STATUS_REQUEST. They can be distinguished * based on the type of question entry. */ /* All query requests have to have question entry */ if (question == 0) break; if (question->question_type == NAME_QUESTION_TYPE_NB) { name = question->name; if ((entry = smb_netbios_cache_lookup(name)) != 0) { /* * send response to the IP address and port * number from which the request was received. */ (void) smb_send_name_query_response(addr, packet, entry, 0); smb_netbios_cache_unlock_entry(entry); } else { /* * send response to the requestor */ (void) smb_send_name_query_response(addr, packet, name, RCODE_NAM_ERR); } } else if (question->question_type == NAME_QUESTION_TYPE_NBSTAT) { /* * Name of "*" may be used to force node to * divulge status for administrative purposes */ name = question->name; entry = 0; if (NETBIOS_NAME_IS_STAR(name->name) || ((entry = smb_netbios_cache_lookup(name)) != 0)) { /* * send only those names that are * in the same scope as the scope * field in the request packet */ if (entry) smb_netbios_cache_unlock_entry(entry); (void) smb_send_node_status_response(addr, packet); } } break; default: break; } } /* * 5.1.3.5. M-NODE INCOMING PACKET PROCESSING * * Processing initiated by reception of packets at a M node * * PROCEDURE process_incoming_packet(packet) * * (* * * Processing initiated by incoming packets at a M node * *) * * BEGIN * CASE packet type of * * NAME CONFLICT DEMAND: * IF name exists in local name table THEN * mark name as in conflict; * return; * * NAME QUERY REQUEST: * IF name exists in local name table THEN * BEGIN (* name exists *) * * (* * * build packet * *) * ... * * (* * * send response to the IP address and port * * number from which the request was received. * *) * * send POSITIVE NAME QUERY RESPONSE ; * return; * END (* exists *) * ELSE * BEGIN (* does not exist *) * * (* * * send response to the requestor * *) * * IF request NOT broadcast THEN * (* * * Don't send negative responses to * * queries sent by B nodes * *) * send NEGATIVE NAME QUERY RESPONSE ; * return; * END (* does not exist *) * NODE STATUS REQUEST: * BEGIN * (* * * Name of "*" may be used to force node to * * divulge status for administrative purposes * *) * IF name in local name table OR name = "*" THEN * (* * * Build response packet and * * send to requestor node * * Send only those names that are * * in the same scope as the scope * * in the request packet. * *) * * send NODE STATUS RESPONSE; * END * * NAME RELEASE REQUEST: * (* * * This will be received if the NAME wants to flush the * * name from the local name table, or from the local * * cache. * *) * * IF name exists in the local name table THEN * BEGIN * delete name from local name table; * inform user that name has been deleted; * END * NAME REGISTRATION REQUEST (UNIQUE): * IF name exists in local name table THEN * send NEGATIVE NAME REGISTRATION RESPONSE ; * NAME REGISTRATION REQUEST (GROUP): * IF name exists in local name table THEN * BEGIN * IF local entry is a unique name THEN * send NEGATIVE NAME REGISTRATION RESPONSE ; * END * END (* case *) * END (* procedure *) */ static void smb_name_process_Mnode_packet(struct name_packet *packet, struct addr_entry *addr) { if (packet->info & NAME_NM_FLAGS_B) smb_name_process_Bnode_packet(packet, addr); else smb_name_process_Pnode_packet(packet, addr); } static void smb_name_process_Hnode_packet(struct name_packet *packet, struct addr_entry *addr) { if (packet->info & NAME_NM_FLAGS_B) smb_name_process_Bnode_packet(packet, addr); else smb_name_process_Pnode_packet(packet, addr); } /* * smb_netbios_name_tick * * Called once a second to handle name server timeouts. */ void smb_netbios_name_tick(void) { struct name_entry *name; struct name_entry *entry; (void) mutex_lock(&refresh_queue.mtx); smb_netbios_cache_refresh(&refresh_queue); while ((name = refresh_queue.head.forw) != &refresh_queue.head) { QUEUE_CLIP(name); if (IS_LOCAL(name->attributes)) { if (IS_UNIQUE(name->attributes)) { (void) smb_name_Pnode_refresh_name(name); } } else { entry = smb_name_find_name(name); smb_name_unlock_name(entry); } free(name); } (void) mutex_unlock(&refresh_queue.mtx); smb_netbios_cache_reset_ttl(); } /* * smb_name_find_name * * Lookup name cache for the given name. * If it's not in the cache it'll send a * name query request and then lookup the * cache again. Note that if a name is * returned it's locked and called MUST * unlock it by calling smb_name_unlock_name() */ struct name_entry * smb_name_find_name(struct name_entry *name) { struct name_entry *result; if ((result = smb_netbios_cache_lookup(name)) == 0) { switch (smb_node_type) { case 'B': (void) smb_name_Bnode_find_name(name); break; case 'P': (void) smb_name_Pnode_find_name(name); break; case 'M': (void) smb_name_Mnode_find_name(name); break; case 'H': default: (void) smb_name_Hnode_find_name(name); break; } return (smb_netbios_cache_lookup(name)); } return (result); } void smb_name_unlock_name(struct name_entry *name) { smb_netbios_cache_unlock_entry(name); } int smb_name_add_name(struct name_entry *name) { int rc = 1; smb_netbios_name_dump(name); switch (smb_node_type) { case 'B': rc = smb_name_Bnode_add_name(name); break; case 'P': rc = smb_name_Pnode_add_name(name); break; case 'M': rc = smb_name_Mnode_add_name(name); break; case 'H': default: rc = smb_name_Hnode_add_name(name); break; } if (rc >= 0) (void) smb_netbios_cache_insert(name); return (rc); } int smb_name_delete_name(struct name_entry *name) { int rc; unsigned char type; type = name->name[15]; if ((type != 0x00) && (type != 0x20)) { syslog(LOG_ERR, "netbios: error trying to delete non-local name"); smb_netbios_name_logf(name); name->attributes &= ~NAME_ATTR_LOCAL; return (-1); } smb_netbios_cache_delete(name); switch (smb_node_type) { case 'B': rc = smb_name_Bnode_delete_name(name); break; case 'P': rc = smb_name_Pnode_delete_name(name); break; case 'M': rc = smb_name_Mnode_delete_name(name); break; case 'H': default: rc = smb_name_Hnode_delete_name(name); break; } if (rc > 0) return (0); return (-1); } typedef struct { struct addr_entry *addr; char *buf; int length; } worker_param_t; /* * smb_netbios_worker * * Process incoming request/response packets for Netbios * name service (on port 138). */ void * smb_netbios_worker(void *arg) { worker_param_t *p = (worker_param_t *)arg; struct addr_entry *addr = p->addr; struct name_packet *packet; if ((packet = smb_name_buf_to_packet(p->buf, p->length)) != 0) { if (packet->info & NAME_OPCODE_R) { /* Reply packet */ smb_reply_ready(packet, addr); free(p->buf); free(p); return (0); } /* Request packet */ switch (smb_node_type) { case 'B': smb_name_process_Bnode_packet(packet, addr); break; case 'P': smb_name_process_Pnode_packet(packet, addr); break; case 'M': smb_name_process_Mnode_packet(packet, addr); break; case 'H': default: smb_name_process_Hnode_packet(packet, addr); break; } if (packet->answer) smb_netbios_name_freeaddrs(packet->answer->name); free(packet); } else { syslog(LOG_DEBUG, "SmbNBNS: error decoding received packet"); } free(addr); free(p->buf); free(p); return (0); } static void smb_netbios_wins_config(char *ip) { uint32_t ipaddr; ipaddr = inet_addr(ip); if (ipaddr != INADDR_NONE) { smb_nbns[nbns_num].flags = ADDR_FLAG_VALID; smb_nbns[nbns_num].sinlen = sizeof (struct sockaddr_in); smb_nbns[nbns_num].sin.sin_family = AF_INET; smb_nbns[nbns_num].sin.sin_addr.s_addr = ipaddr; smb_nbns[nbns_num++].sin.sin_port = htons(NAME_SERVICE_UDP_PORT); smb_node_type = SMB_NODETYPE_H; } } static void smb_netbios_name_registration(void) { nbcache_iter_t nbc_iter; struct name_entry *name; int rc; rc = smb_netbios_cache_getfirst(&nbc_iter); while (rc == 0) { name = nbc_iter.nbc_entry; (void) smb_netbios_name_logf(name); if (IS_UNIQUE(name->attributes) && IS_LOCAL(name->attributes)) { switch (smb_node_type) { case SMB_NODETYPE_B: (void) smb_name_Bnode_add_name(name); break; case SMB_NODETYPE_P: (void) smb_name_Pnode_add_name(name); break; case SMB_NODETYPE_M: (void) smb_name_Mnode_add_name(name); break; case SMB_NODETYPE_H: default: (void) smb_name_Hnode_add_name(name); break; } } free(name); rc = smb_netbios_cache_getnext(&nbc_iter); } } void smb_netbios_name_config(void) { struct name_entry name; char wins_ip[16]; smb_niciter_t ni; int rc; /* Start with no broadcast addresses */ bcast_num = 0; bzero(smb_bcast_list, sizeof (addr_entry_t) * SMB_PI_MAX_NETWORKS); /* Add all of the broadcast addresses */ rc = smb_nic_getfirst(&ni); while (rc == 0) { if (ni.ni_nic.nic_smbflags & (SMB_NICF_ALIAS | SMB_NICF_NBEXCL)) { rc = smb_nic_getnext(&ni); continue; } smb_bcast_list[bcast_num].flags = ADDR_FLAG_VALID; smb_bcast_list[bcast_num].attributes = NAME_ATTR_LOCAL; smb_bcast_list[bcast_num].sinlen = sizeof (struct sockaddr_in); smb_bcast_list[bcast_num].sin.sin_family = AF_INET; smb_bcast_list[bcast_num].sin.sin_port = htons(NAME_SERVICE_UDP_PORT); smb_bcast_list[bcast_num++].sin.sin_addr.s_addr = ni.ni_nic.nic_bcast; rc = smb_nic_getnext(&ni); } /* Start with no WINS */ smb_node_type = SMB_NODETYPE_B; nbns_num = 0; bzero(smb_nbns, sizeof (addr_entry_t) * SMB_PI_MAX_WINS); /* add any configured WINS */ (void) smb_config_getstr(SMB_CI_WINS_SRV1, wins_ip, sizeof (wins_ip)); smb_netbios_wins_config(wins_ip); (void) smb_config_getstr(SMB_CI_WINS_SRV2, wins_ip, sizeof (wins_ip)); smb_netbios_wins_config(wins_ip); if (smb_nic_getfirst(&ni) != 0) return; do { if (ni.ni_nic.nic_smbflags & SMB_NICF_NBEXCL) continue; smb_init_name_struct((unsigned char *)ni.ni_nic.nic_host, 0x00, 0, ni.ni_nic.nic_ip, htons(DGM_SRVC_UDP_PORT), NAME_ATTR_UNIQUE, NAME_ATTR_LOCAL, &name); (void) smb_netbios_cache_insert(&name); smb_init_name_struct((unsigned char *)ni.ni_nic.nic_host, 0x20, 0, ni.ni_nic.nic_ip, htons(DGM_SRVC_UDP_PORT), NAME_ATTR_UNIQUE, NAME_ATTR_LOCAL, &name); (void) smb_netbios_cache_insert(&name); } while (smb_nic_getnext(&ni) == 0); smb_netbios_name_registration(); } void smb_netbios_name_unconfig(void) { struct name_entry *name; (void) mutex_lock(&delete_queue.mtx); smb_netbios_cache_delete_locals(&delete_queue); while ((name = delete_queue.head.forw) != &delete_queue.head) { QUEUE_CLIP(name); (void) smb_name_delete_name(name); free(name); } (void) mutex_unlock(&delete_queue.mtx); } void smb_netbios_name_reconfig(void) { smb_netbios_name_unconfig(); smb_netbios_name_config(); } /* * process_incoming Function: void smb_netbios_name_service_daemon(void) * * Description: * * Put test description here. * * Inputs: * Nothing * * Returns: * int -> Description */ /*ARGSUSED*/ void * smb_netbios_name_service_daemon(void *arg) { struct sockaddr_in sin; struct addr_entry *addr; int len; int flag = 1; char *buf; worker_param_t *worker_param; /* * Initialize reply_queue */ bzero(&reply_queue, sizeof (reply_queue)); reply_queue.forw = reply_queue.back = &reply_queue; if (!smb_netbios_cache_init()) return (0); bcast_num = 0; bzero(smb_bcast_list, sizeof (addr_entry_t) * SMB_PI_MAX_NETWORKS); if ((name_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { syslog(LOG_ERR, "smbd: Could not create AF_INET, SOCK_DGRAM, socket"); smb_netbios_cache_fini(); smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1); return (0); } (void) setsockopt(name_sock, SOL_SOCKET, SO_BROADCAST, &flag, sizeof (flag)); bzero(&sin, sizeof (struct sockaddr_in)); sin.sin_family = AF_INET; sin.sin_port = htons(NAME_SERVICE_UDP_PORT); if (bind(name_sock, (struct sockaddr *)&sin, sizeof (sin)) != 0) { syslog(LOG_ERR, "smbd: Bind to name service port %d failed (%d)", NAME_SERVICE_UDP_PORT, errno); smb_netbios_cache_fini(); (void) close(name_sock); smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1); return (0); } smb_netbios_chg_status(NETBIOS_NAME_SVC_RUNNING, 1); while (((nb_status.state & NETBIOS_SHUTTING_DOWN) == 0) || (nb_status.state & NETBIOS_BROWSER_RUNNING)) { if ((buf = malloc(MAX_DATAGRAM_LENGTH)) == 0) { /* Sleep for 10 sec and try again */ (void) sleep(10); continue; } if ((addr = (struct addr_entry *) malloc(sizeof (struct addr_entry))) == 0) { /* Sleep for 10 sec and try again */ free(buf); (void) sleep(10); continue; } ignore: bzero(addr, sizeof (struct addr_entry)); addr->sinlen = sizeof (addr->sin); addr->forw = addr->back = addr; if ((len = recvfrom(name_sock, buf, MAX_DATAGRAM_LENGTH, 0, (struct sockaddr *)&addr->sin, &addr->sinlen)) < 0) { if (errno == ENOMEM || errno == ENFILE || errno == EMFILE) { /* Sleep for 10 sec and try again */ free(buf); free(addr); (void) sleep(10); continue; } syslog(LOG_ERR, "smbd: NETBIOS name service - recvfrom failed"); free(buf); free(addr); smb_netbios_chg_status(NETBIOS_NAME_SVC_FAILED, 1); goto shutdown; } /* Ignore any incoming packets from myself... */ if (smb_nic_exists(addr->sin.sin_addr.s_addr, B_FALSE)) goto ignore; /* * Launch a netbios worker to process the received packet. */ worker_param = (worker_param_t *) malloc(sizeof (worker_param_t)); if (worker_param) { pthread_t worker; pthread_attr_t tattr; worker_param->addr = addr; worker_param->buf = buf; worker_param->length = len; (void) pthread_attr_init(&tattr); (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); (void) pthread_create(&worker, &tattr, smb_netbios_worker, worker_param); (void) pthread_attr_destroy(&tattr); } } shutdown: smb_netbios_chg_status(NETBIOS_NAME_SVC_RUNNING, 0); (void) mutex_lock(&nb_status.mtx); while (nb_status.state & NETBIOS_BROWSER_RUNNING) (void) cond_wait(&nb_status.cv, &nb_status.mtx); (void) mutex_unlock(&nb_status.mtx); if ((nb_status.state & NETBIOS_NAME_SVC_FAILED) == 0) { /* this might delay shutdown, do we want to do this? */ /* * it'll send name release requests but nobody's waiting * for response and it'll eventually timeout. */ smb_netbios_name_unconfig(); } (void) close(name_sock); smb_netbios_cache_fini(); syslog(LOG_DEBUG, "smbd: Netbios Name Service is down\n"); return (0); }