/** * SMB2/3 Server - Main Entry Point * TypeScript implementation of SMB2/3 protocol server */ import { SMB2Server } from './server'; import type { SMB2ServerConfig } from './protocol/types'; import { SMB2_DIALECTS, SHARE_TYPE, SHARE_FLAGS, SHARE_CAPABILITIES, ACCESS_MASK } from './protocol/constants'; import { Logger, setGlobalLogLevel } from './utils/logger'; import { getEnvVar, getEnvNumber, getEnvBoolean, generateGuid } from './utils/helpers'; // Global logger const logger = new Logger('SMB2Main'); /** * Create default server configuration */ function createDefaultConfig(): SMB2ServerConfig { return { serverGuid: generateGuid(), serverName: getEnvVar('SMB_SERVER_NAME', 'TypeScript-SMB-Server'), domainName: getEnvVar('SMB_DOMAIN_NAME'), workgroupName: getEnvVar('SMB_WORKGROUP_NAME', 'WORKGROUP'), supportedDialects: [ SMB2_DIALECTS.SMB2_0_2, SMB2_DIALECTS.SMB2_1_0, SMB2_DIALECTS.SMB3_0_0, SMB2_DIALECTS.SMB3_0_2, SMB2_DIALECTS.SMB3_1_1, ], requireMessageSigning: getEnvBoolean('SMB_REQUIRE_SIGNING', false), enableEncryption: getEnvBoolean('SMB_ENABLE_ENCRYPTION', false), maxConnections: getEnvNumber('SMB_MAX_CONNECTIONS', 100), maxSessionsPerConnection: getEnvNumber('SMB_MAX_SESSIONS_PER_CONNECTION', 16), sessionTimeout: getEnvNumber('SMB_SESSION_TIMEOUT', 300000), // 5 minutes maxTransactSize: getEnvNumber('SMB_MAX_TRANSACT_SIZE', 65536), maxReadSize: getEnvNumber('SMB_MAX_READ_SIZE', 65536), maxWriteSize: getEnvNumber('SMB_MAX_WRITE_SIZE', 65536), // Default shares configuration shares: [ { name: 'Public', path: getEnvVar('SMB_SHARE_PATH', './shared'), shareType: SHARE_TYPE.DISK, shareFlags: SHARE_FLAGS.AUTO_CACHING | SHARE_FLAGS.ACCESS_BASED_DIRECTORY_ENUM, capabilities: SHARE_CAPABILITIES.DFS, maximalAccess: ACCESS_MASK.GENERIC_ALL, encryptData: false, cachingPolicy: 0, description: 'Public file share', accessControl: [], }, ], // Default users configuration (empty - authentication disabled by default) users: [], }; } /** * Setup process event handlers */ function setupProcessHandlers(server: SMB2Server): void { let isShuttingDown = false; const gracefulShutdown = async (signal: string): Promise => { if (isShuttingDown) { logger.warn('Force shutdown requested'); process.exit(1); } isShuttingDown = true; logger.info(`Received ${signal}, shutting down gracefully...`); try { await server.shutdown(); logger.info('Server shutdown complete'); process.exit(0); } catch (error) { logger.error('Error during shutdown:', error); process.exit(1); } }; // Handle graceful shutdown signals process.on('SIGINT', () => gracefulShutdown('SIGINT')); process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); // Handle uncaught exceptions process.on('uncaughtException', (error) => { logger.error('Uncaught exception:', error); gracefulShutdown('uncaughtException'); }); // Handle unhandled promise rejections process.on('unhandledRejection', (reason, promise) => { logger.error('Unhandled promise rejection:', { reason, promise }); gracefulShutdown('unhandledRejection'); }); } /** * Setup server event handlers */ function setupServerHandlers(server: SMB2Server): void { // Connection events server.onEvent('connection:established', (event) => { logger.info(`New client connected: ${event.data.connection.clientAddress}`); }); server.onEvent('connection:closed', (event) => { logger.info(`Client disconnected: ${event.data.connectionId} (${event.data.reason})`); }); // Session events server.onEvent('session:established', (event) => { logger.info(`Session established: ${event.data.sessionId} for user ${event.data.userId}`); }); server.onEvent('session:closed', (event) => { logger.info(`Session closed: ${event.data.sessionId} (${event.data.reason})`); }); // Tree connect events server.onEvent('tree:connected', (event) => { logger.info(`Tree connected: ${event.data.shareName} (ID: ${event.data.treeConnectId})`); }); server.onEvent('tree:disconnected', (event) => { logger.info(`Tree disconnected: ${event.data.treeConnectId} (${event.data.reason})`); }); // File operation events server.onEvent('file:opened', (event) => { logger.debug(`File opened: ${event.data.fileName}`); }); server.onEvent('file:closed', (event) => { logger.debug(`File closed: ${event.data.fileName}`); }); server.onEvent('file:read', (event) => { logger.debug(`File read: ${event.data.fileName} (${event.data.bytes} bytes)`); }); server.onEvent('file:written', (event) => { logger.debug(`File written: ${event.data.fileName} (${event.data.bytes} bytes)`); }); // Error events server.onEvent('error', (event) => { logger.error('Server error:', event.data.error); }); server.onEvent('warning', (event) => { logger.warn('Server warning:', event.data); }); } /** * Main server startup function */ async function startServer(): Promise { try { // Set log level from environment const logLevel = getEnvVar('LOG_LEVEL', 'info') as any; setGlobalLogLevel(logLevel); logger.info('Starting SMB2/3 Server...'); // Create server configuration const config = createDefaultConfig(); logger.info(`Server configuration:`); logger.info(` Server Name: ${config.serverName}`); logger.info(` Server GUID: ${config.serverGuid}`); logger.info(` Supported Dialects: ${config.supportedDialects.map(d => `0x${d.toString(16).toUpperCase()}`).join(', ')}`); logger.info(` Require Signing: ${config.requireMessageSigning}`); logger.info(` Enable Encryption: ${config.enableEncryption}`); logger.info(` Max Connections: ${config.maxConnections}`); logger.info(` Shares: ${config.shares.map(s => `${s.name} (${s.path})`).join(', ')}`); // Create and configure server const server = new SMB2Server(config); // Setup event handlers setupServerHandlers(server); setupProcessHandlers(server); // Start server const port = getEnvNumber('SMB_PORT', 445); const hostname = getEnvVar('SMB_HOST'); await server.start(port, hostname); logger.info('SMB2/3 Server started successfully!'); logger.info(`Listening on ${hostname || '0.0.0.0'}:${port}`); logger.info('Press Ctrl+C to shutdown'); // Log server stats periodically setInterval(() => { const stats = server.getStats(); logger.debug(`Server stats: ${stats.connections} connections, ${stats.sessions} sessions, uptime: ${Math.round(stats.uptime / 1000)}s`); }, 60000); // Every minute } catch (error) { logger.error('Failed to start server:', error); process.exit(1); } } /** * Display help information */ function showHelp(): void { console.log(` SMB2/3 Server - TypeScript Implementation Usage: npm start [options] Environment Variables: LOG_LEVEL Set logging level (error, warn, info, debug) [default: info] SMB_PORT Server port [default: 445] SMB_HOST Server hostname/IP [default: 0.0.0.0] SMB_SERVER_NAME Server name [default: TypeScript-SMB-Server] SMB_DOMAIN_NAME Domain name [optional] SMB_WORKGROUP_NAME Workgroup name [default: WORKGROUP] SMB_REQUIRE_SIGNING Require message signing [default: false] SMB_ENABLE_ENCRYPTION Enable SMB encryption [default: false] SMB_MAX_CONNECTIONS Maximum concurrent connections [default: 100] SMB_MAX_SESSIONS_PER_CONNECTION Max sessions per connection [default: 16] SMB_SESSION_TIMEOUT Session timeout in ms [default: 300000] SMB_MAX_TRANSACT_SIZE Maximum transaction size [default: 65536] SMB_MAX_READ_SIZE Maximum read size [default: 65536] SMB_MAX_WRITE_SIZE Maximum write size [default: 65536] SMB_SHARE_PATH Default share path [default: ./shared] Examples: npm start Start with default configuration LOG_LEVEL=debug npm start Start with debug logging SMB_PORT=8445 npm start Start on custom port Note: Running on port 445 typically requires administrator/root privileges. `); } // Check for help flag if (process.argv.includes('--help') || process.argv.includes('-h')) { showHelp(); process.exit(0); } // Start the server startServer().catch((error) => { logger.error('Startup error:', error); process.exit(1); }); // Export for programmatic use export { SMB2Server, createDefaultConfig }; export * from './protocol/types'; export * from './protocol/constants';