1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * Main startup code for SMB/NETBIOS and some utility routines
28 * for the NETBIOS layer.
29 */
30
31#include <sys/tzfile.h>
32#include <assert.h>
33#include <synch.h>
34#include <unistd.h>
35#include <syslog.h>
36#include <string.h>
37#include <strings.h>
38#include <sys/socket.h>
39#include <stdio.h>
40#include <pwd.h>
41#include <grp.h>
42#include <smbns_netbios.h>
43
44#define	SMB_NETBIOS_DUMP_FILE		"netbios"
45
46static netbios_service_t nbtd;
47
48static void smb_netbios_shutdown(void);
49static void *smb_netbios_service(void *);
50static void smb_netbios_dump(void);
51
52/*
53 * Start the NetBIOS services
54 */
55int
56smb_netbios_start(void)
57{
58	pthread_t	tid;
59	pthread_attr_t	attr;
60	int		rc;
61
62	if (smb_netbios_cache_init() < 0)
63		return (-1);
64
65	(void) pthread_attr_init(&attr);
66	(void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
67	rc = pthread_create(&tid, &attr, smb_netbios_service, NULL);
68	(void) pthread_attr_destroy(&attr);
69	return (rc);
70}
71
72/*
73 * Stop the NetBIOS services
74 */
75void
76smb_netbios_stop(void)
77{
78	char	fname[MAXPATHLEN];
79
80	smb_netbios_event(NETBIOS_EVENT_STOP);
81
82	(void) snprintf(fname, MAXPATHLEN, "%s/%s",
83	    SMB_VARRUN_DIR, SMB_NETBIOS_DUMP_FILE);
84	(void) unlink(fname);
85
86}
87
88/*
89 * Launch the NetBIOS Name Service, Datagram and Browser services
90 * and then sit in a loop providing a 1 second resolution timer.
91 * The timer will:
92 *	- update the netbios stats file every 10 minutes
93 *	- clean the cache every 10 minutes
94 */
95/*ARGSUSED*/
96static void *
97smb_netbios_service(void *arg)
98{
99	static uint32_t	ticks = 0;
100	pthread_t	tid;
101	int		rc;
102
103	smb_netbios_event(NETBIOS_EVENT_START);
104
105	rc = pthread_create(&tid, NULL, smb_netbios_name_service, NULL);
106	if (rc != 0) {
107		smb_netbios_shutdown();
108		return (NULL);
109	}
110
111	smb_netbios_wait(NETBIOS_EVENT_NS_START);
112	if (smb_netbios_error()) {
113		smb_netbios_shutdown();
114		return (NULL);
115	}
116
117	smb_netbios_name_config();
118
119	rc = pthread_create(&tid, NULL, smb_netbios_datagram_service, NULL);
120	if (rc != 0) {
121		smb_netbios_shutdown();
122		return (NULL);
123	}
124
125	smb_netbios_wait(NETBIOS_EVENT_DGM_START);
126	if (smb_netbios_error()) {
127		smb_netbios_shutdown();
128		return (NULL);
129	}
130
131	rc = pthread_create(&tid, NULL, smb_browser_service, NULL);
132	if (rc != 0) {
133		smb_netbios_shutdown();
134		return (NULL);
135	}
136
137	smb_netbios_event(NETBIOS_EVENT_TIMER_START);
138
139	for (;;) {
140		(void) sleep(1);
141		ticks++;
142
143		if (!smb_netbios_running())
144			break;
145
146		smb_netbios_datagram_tick();
147		smb_netbios_name_tick();
148
149		if ((ticks % 600) == 0) {
150			smb_netbios_event(NETBIOS_EVENT_DUMP);
151			smb_netbios_cache_clean();
152		}
153	}
154
155	smb_netbios_event(NETBIOS_EVENT_TIMER_STOP);
156	smb_netbios_shutdown();
157	return (NULL);
158}
159
160static void
161smb_netbios_shutdown(void)
162{
163	(void) pthread_join(nbtd.nbs_browser.s_tid, 0);
164	(void) pthread_join(nbtd.nbs_dgm.s_tid, 0);
165	(void) pthread_join(nbtd.nbs_ns.s_tid, 0);
166
167	nbtd.nbs_browser.s_tid = 0;
168	nbtd.nbs_dgm.s_tid = 0;
169	nbtd.nbs_ns.s_tid = 0;
170
171	smb_netbios_cache_fini();
172
173	if (smb_netbios_error()) {
174		smb_netbios_event(NETBIOS_EVENT_RESET);
175		if (smb_netbios_start() != 0)
176			syslog(LOG_ERR, "netbios: restart failed");
177	}
178}
179
180int
181smb_first_level_name_encode(struct name_entry *name,
182				unsigned char *out, int max_out)
183{
184	return (netbios_first_level_name_encode(name->name, name->scope,
185	    out, max_out));
186}
187
188int
189smb_first_level_name_decode(unsigned char *in, struct name_entry *name)
190{
191	return (netbios_first_level_name_decode((char *)in, (char *)name->name,
192	    (char *)name->scope));
193}
194
195/*
196 * smb_encode_netbios_name
197 *
198 * Set up the name and scope fields in the destination name_entry structure.
199 * The name is padded with spaces to 15 bytes. The suffix is copied into the
200 * last byte, i.e. "netbiosname    <suffix>". The scope is copied and folded
201 * to uppercase.
202 */
203void
204smb_encode_netbios_name(unsigned char *name, char suffix, unsigned char *scope,
205    struct name_entry *dest)
206{
207	smb_tonetbiosname((char *)name, (char *)dest->name, suffix);
208
209	if (scope) {
210		(void) strlcpy((char *)dest->scope, (const char *)scope,
211		    sizeof (dest->scope));
212	} else {
213		(void) smb_config_getstr(SMB_CI_NBSCOPE, (char *)dest->scope,
214		    sizeof (dest->scope));
215	}
216
217	(void) smb_strupr((char *)dest->scope);
218}
219
220void
221smb_init_name_struct(unsigned char *name, char suffix, unsigned char *scope,
222    uint32_t ipaddr, unsigned short port, uint32_t attr,
223    uint32_t addr_attr, struct name_entry *dest)
224{
225	bzero(dest, sizeof (struct name_entry));
226	smb_encode_netbios_name(name, suffix, scope, dest);
227
228	switch (smb_node_type) {
229	case 'H':
230		dest->attributes = attr | NAME_ATTR_OWNER_TYPE_HNODE;
231		break;
232	case 'M':
233		dest->attributes = attr | NAME_ATTR_OWNER_TYPE_MNODE;
234		break;
235	case 'P':
236		dest->attributes = attr | NAME_ATTR_OWNER_TYPE_PNODE;
237		break;
238	case 'B':
239	default:
240		dest->attributes = attr | NAME_ATTR_OWNER_TYPE_BNODE;
241		break;
242	}
243
244	dest->addr_list.refresh_ttl = dest->addr_list.ttl =
245	    TO_SECONDS(DEFAULT_TTL);
246
247	dest->addr_list.sin.sin_family = AF_INET;
248	dest->addr_list.sinlen = sizeof (dest->addr_list.sin);
249	dest->addr_list.sin.sin_addr.s_addr = ipaddr;
250	dest->addr_list.sin.sin_port = port;
251	dest->addr_list.attributes = addr_attr;
252	dest->addr_list.forw = dest->addr_list.back = &dest->addr_list;
253}
254
255void
256smb_netbios_event(netbios_event_t event)
257{
258	static char *event_msg[] = {
259		"startup",
260		"shutdown",
261		"restart",
262		"name service started",
263		"name service stopped",
264		"datagram service started",
265		"datagram service stopped",
266		"browser service started",
267		"browser service stopped",
268		"timer service started",
269		"timer service stopped",
270		"error",
271		"dump"
272	};
273
274	(void) mutex_lock(&nbtd.nbs_mtx);
275
276	if (event == NETBIOS_EVENT_DUMP) {
277		if (nbtd.nbs_last_event == NULL)
278			nbtd.nbs_last_event = event_msg[event];
279		smb_netbios_dump();
280		(void) mutex_unlock(&nbtd.nbs_mtx);
281		return;
282	}
283
284	nbtd.nbs_last_event = event_msg[event];
285	syslog(LOG_DEBUG, "netbios: %s", nbtd.nbs_last_event);
286
287	switch (nbtd.nbs_state) {
288	case NETBIOS_STATE_INIT:
289		if (event == NETBIOS_EVENT_START)
290			nbtd.nbs_state = NETBIOS_STATE_RUNNING;
291		break;
292
293	case NETBIOS_STATE_RUNNING:
294		switch (event) {
295		case NETBIOS_EVENT_NS_START:
296			nbtd.nbs_ns.s_tid = pthread_self();
297			nbtd.nbs_ns.s_up = B_TRUE;
298			break;
299		case NETBIOS_EVENT_NS_STOP:
300			nbtd.nbs_ns.s_up = B_FALSE;
301			break;
302		case NETBIOS_EVENT_DGM_START:
303			nbtd.nbs_dgm.s_tid = pthread_self();
304			nbtd.nbs_dgm.s_up = B_TRUE;
305			break;
306		case NETBIOS_EVENT_DGM_STOP:
307			nbtd.nbs_dgm.s_up = B_FALSE;
308			break;
309		case NETBIOS_EVENT_BROWSER_START:
310			nbtd.nbs_browser.s_tid = pthread_self();
311			nbtd.nbs_browser.s_up = B_TRUE;
312			break;
313		case NETBIOS_EVENT_BROWSER_STOP:
314			nbtd.nbs_browser.s_up = B_FALSE;
315			break;
316		case NETBIOS_EVENT_TIMER_START:
317			nbtd.nbs_timer.s_tid = pthread_self();
318			nbtd.nbs_timer.s_up = B_TRUE;
319			break;
320		case NETBIOS_EVENT_TIMER_STOP:
321			nbtd.nbs_timer.s_up = B_FALSE;
322			break;
323		case NETBIOS_EVENT_STOP:
324			nbtd.nbs_state = NETBIOS_STATE_CLOSING;
325			break;
326		case NETBIOS_EVENT_ERROR:
327			nbtd.nbs_state = NETBIOS_STATE_ERROR;
328			++nbtd.nbs_errors;
329			break;
330		default:
331			break;
332		}
333		break;
334
335	case NETBIOS_STATE_CLOSING:
336	case NETBIOS_STATE_ERROR:
337	default:
338		switch (event) {
339		case NETBIOS_EVENT_NS_STOP:
340			nbtd.nbs_ns.s_up = B_FALSE;
341			break;
342		case NETBIOS_EVENT_DGM_STOP:
343			nbtd.nbs_dgm.s_up = B_FALSE;
344			break;
345		case NETBIOS_EVENT_BROWSER_STOP:
346			nbtd.nbs_browser.s_up = B_FALSE;
347			break;
348		case NETBIOS_EVENT_TIMER_STOP:
349			nbtd.nbs_timer.s_up = B_FALSE;
350			break;
351		case NETBIOS_EVENT_STOP:
352			nbtd.nbs_state = NETBIOS_STATE_CLOSING;
353			break;
354		case NETBIOS_EVENT_RESET:
355			nbtd.nbs_state = NETBIOS_STATE_INIT;
356			break;
357		case NETBIOS_EVENT_ERROR:
358			++nbtd.nbs_errors;
359			break;
360		default:
361			break;
362		}
363		break;
364	}
365
366	smb_netbios_dump();
367	(void) cond_broadcast(&nbtd.nbs_cv);
368	(void) mutex_unlock(&nbtd.nbs_mtx);
369}
370
371void
372smb_netbios_wait(netbios_event_t event)
373{
374	boolean_t *svc = NULL;
375	boolean_t desired_state;
376
377	(void) mutex_lock(&nbtd.nbs_mtx);
378
379	switch (event) {
380	case NETBIOS_EVENT_NS_START:
381	case NETBIOS_EVENT_NS_STOP:
382		svc = &nbtd.nbs_ns.s_up;
383		desired_state =
384		    (event == NETBIOS_EVENT_NS_START) ? B_TRUE : B_FALSE;
385		break;
386	case NETBIOS_EVENT_DGM_START:
387	case NETBIOS_EVENT_DGM_STOP:
388		svc = &nbtd.nbs_dgm.s_up;
389		desired_state =
390		    (event == NETBIOS_EVENT_DGM_START) ? B_TRUE : B_FALSE;
391		break;
392	case NETBIOS_EVENT_BROWSER_START:
393	case NETBIOS_EVENT_BROWSER_STOP:
394		svc = &nbtd.nbs_browser.s_up;
395		desired_state =
396		    (event == NETBIOS_EVENT_BROWSER_START) ? B_TRUE : B_FALSE;
397		break;
398	default:
399		(void) mutex_unlock(&nbtd.nbs_mtx);
400		return;
401	}
402
403	while (*svc != desired_state) {
404		if (nbtd.nbs_state != NETBIOS_STATE_RUNNING)
405			break;
406
407		(void) cond_wait(&nbtd.nbs_cv, &nbtd.nbs_mtx);
408	}
409
410	(void) mutex_unlock(&nbtd.nbs_mtx);
411}
412
413void
414smb_netbios_sleep(time_t seconds)
415{
416	timestruc_t reltimeout;
417
418	(void) mutex_lock(&nbtd.nbs_mtx);
419
420	if (nbtd.nbs_state == NETBIOS_STATE_RUNNING) {
421		if (seconds == 0)
422			seconds  = 1;
423		reltimeout.tv_sec = seconds;
424		reltimeout.tv_nsec = 0;
425
426		(void) cond_reltimedwait(&nbtd.nbs_cv,
427		    &nbtd.nbs_mtx, &reltimeout);
428	}
429
430	(void) mutex_unlock(&nbtd.nbs_mtx);
431}
432
433boolean_t
434smb_netbios_running(void)
435{
436	boolean_t is_running;
437
438	(void) mutex_lock(&nbtd.nbs_mtx);
439
440	if (nbtd.nbs_state == NETBIOS_STATE_RUNNING)
441		is_running = B_TRUE;
442	else
443		is_running = B_FALSE;
444
445	(void) mutex_unlock(&nbtd.nbs_mtx);
446	return (is_running);
447}
448
449boolean_t
450smb_netbios_error(void)
451{
452	boolean_t error;
453
454	(void) mutex_lock(&nbtd.nbs_mtx);
455
456	if (nbtd.nbs_state == NETBIOS_STATE_ERROR)
457		error = B_TRUE;
458	else
459		error = B_FALSE;
460
461	(void) mutex_unlock(&nbtd.nbs_mtx);
462	return (error);
463}
464
465/*
466 * Write the service state to /var/run/smb/netbios.
467 *
468 * This is a private interface.  To update the file use:
469 *	smb_netbios_event(NETBIOS_EVENT_DUMP);
470 */
471static void
472smb_netbios_dump(void)
473{
474	static struct {
475		netbios_state_t state;
476		char		*text;
477	} sm[] = {
478		{ NETBIOS_STATE_INIT,		"init" },
479		{ NETBIOS_STATE_RUNNING,	"running" },
480		{ NETBIOS_STATE_CLOSING,	"closing" },
481		{ NETBIOS_STATE_ERROR,		"error" }
482	};
483
484	char		fname[MAXPATHLEN];
485	FILE		*fp;
486	struct passwd	*pwd;
487	struct group	*grp;
488	uid_t		uid;
489	gid_t		gid;
490	char		*last_event = "none";
491	int		i;
492
493	(void) snprintf(fname, MAXPATHLEN, "%s/%s",
494	    SMB_VARRUN_DIR, SMB_NETBIOS_DUMP_FILE);
495
496	if ((fp = fopen(fname, "w")) == NULL)
497		return;
498
499	pwd = getpwnam("root");
500	grp = getgrnam("sys");
501	uid = (pwd == NULL) ? 0 : pwd->pw_uid;
502	gid = (grp == NULL) ? 3 : grp->gr_gid;
503
504	(void) lockf(fileno(fp), F_LOCK, 0);
505	(void) fchmod(fileno(fp), 0600);
506	(void) fchown(fileno(fp), uid, gid);
507
508	if (nbtd.nbs_last_event)
509		last_event = nbtd.nbs_last_event;
510
511	for (i = 0; i < sizeof (sm) / sizeof (sm[0]); ++i) {
512		if (nbtd.nbs_state == sm[i].state) {
513			(void) fprintf(fp,
514			    "State             %s  (event: %s, errors: %u)\n",
515			    sm[i].text, last_event, nbtd.nbs_errors);
516			break;
517		}
518	}
519
520	(void) fprintf(fp, "Name Service      %-7s  (%u)\n",
521	    nbtd.nbs_ns.s_up ? "up" : "down", nbtd.nbs_ns.s_tid);
522	(void) fprintf(fp, "Datagram Service  %-7s  (%u)\n",
523	    nbtd.nbs_dgm.s_up ? "up" : "down", nbtd.nbs_dgm.s_tid);
524	(void) fprintf(fp, "Browser Service   %-7s  (%u)\n",
525	    nbtd.nbs_browser.s_up ? "up" : "down", nbtd.nbs_browser.s_tid);
526	(void) fprintf(fp, "Timer Service     %-7s  (%u)\n",
527	    nbtd.nbs_timer.s_up ? "up" : "down", nbtd.nbs_timer.s_tid);
528
529	smb_netbios_cache_dump(fp);
530
531	(void) lockf(fileno(fp), F_ULOCK, 0);
532	(void) fclose(fp);
533}
534