public static void Main(string[] args) { string hostname = "127.0.0.1"; if (args.Length > 0) { hostname = args[0]; Console.WriteLine("Using hostname: " + hostname); } Console.WriteLine("Using lib60870.NET version " + LibraryCommon.GetLibraryVersionString()); // Own certificate has to be a pfx file that contains the private key X509Certificate2 ownCertificate = new X509Certificate2("client1.pfx"); // Create a new security information object to configure TLS TlsSecurityInformation secInfo = new TlsSecurityInformation(null, ownCertificate); // Add allowed server certificates - not required when AllowOnlySpecificCertificates == false secInfo.AddAllowedCertificate(new X509Certificate2("server.cer")); // Add a CA certificate to check the certificate provided by the server - not required when ChainValidation == false secInfo.AddCA(new X509Certificate2("root.cer")); // Check if the certificate is signed by a provided CA secInfo.ChainValidation = true; // Check that the shown server certificate is in the list of allowed certificates secInfo.AllowOnlySpecificCertificates = true; Connection con = new Connection(hostname); // Set security information object, this will force the connection using TLS (using TCP port 19998) con.SetTlsSecurity(secInfo); con.DebugOutput = true; con.SetASDUReceivedHandler(asduReceivedHandler, null); con.SetConnectionHandler(ConnectionHandler, null); con.Connect(); Thread.Sleep(5000); con.SendTestCommand(1); con.SendInterrogationCommand(CauseOfTransmission.ACTIVATION, 1, QualifierOfInterrogation.STATION); Thread.Sleep(5000); con.SendControlCommand(CauseOfTransmission.ACTIVATION, 1, new SingleCommand(5000, true, false, 0)); con.SendControlCommand(CauseOfTransmission.ACTIVATION, 1, new DoubleCommand(5001, DoubleCommand.ON, false, 0)); con.SendControlCommand(CauseOfTransmission.ACTIVATION, 1, new StepCommand(5002, StepCommandValue.HIGHER, false, 0)); con.SendControlCommand(CauseOfTransmission.ACTIVATION, 1, new SingleCommandWithCP56Time2a(5000, false, false, 0, new CP56Time2a(DateTime.Now))); /* Synchronize clock of the controlled station */ con.SendClockSyncCommand(1 /* CA */, new CP56Time2a(DateTime.Now)); Console.WriteLine("CLOSE"); con.Close(); Console.WriteLine("RECONNECT"); con.Connect(); Thread.Sleep(5000); Console.WriteLine("CLOSE 2"); con.Close(); Console.WriteLine("Press any key to terminate..."); Console.ReadKey(); }
static void Main(string[] args) { Log("{json:scada} IEC60870-5-104 Driver - Copyright 2020 RLO"); Log("Driver version " + DriverVersion); Log("Using lib60870.NET version " + LibraryCommon.GetLibraryVersionString()); if (args.Length > 0) // first argument in number of the driver instance { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if (args.Length > 1) // second argument is logLevel { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); var Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <IEC10X_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (IEC10X_connection isrv in conns) { if (isrv.ipAddresses.Length < 1) { Log("Missing ipAddresses list!"); Environment.Exit(-1); } IEC10Xconns.Add(isrv); Log(isrv.name.ToString() + " - New Connection"); } if (IEC10Xconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to process redundancy control Thread thrMongoRedundacy = new Thread(() => ProcessRedundancyMongo(JSConfig)); thrMongoRedundacy.Start(); // start thread to update acquired data to database Thread thrMongo = new Thread(() => ProcessMongo(JSConfig)); thrMongo.Start(); // start thread to watch for commands in the database using a change stream Thread thrMongoCmd = new Thread(() => ProcessMongoCmd(JSConfig)); thrMongoCmd.Start(); Log("Setting up IEC Connections & ASDU handlers..."); int cntIecSrv = 0; foreach (IEC10X_connection srv in IEC10Xconns) { var apcipars = new APCIParameters(); apcipars.K = srv.k; apcipars.W = srv.w; apcipars.T0 = srv.t0; apcipars.T1 = srv.t1; apcipars.T2 = srv.t2; apcipars.T3 = srv.t3; var alpars = new ApplicationLayerParameters(); alpars.SizeOfCOT = srv.sizeOfCOT; alpars.SizeOfCA = srv.sizeOfCA; alpars.SizeOfIOA = srv.sizeOfIOA; alpars.OA = srv.localLinkAddress; TlsSecurityInformation secInfo = null; if (srv.localCertFilePath != "") { try { // Own certificate has to be a pfx file that contains the private key X509Certificate2 ownCertificate = new X509Certificate2(srv.localCertFilePath); // Create a new security information object to configure TLS secInfo = new TlsSecurityInformation(null, ownCertificate); // Add allowed server certificates - not required when AllowOnlySpecificCertificates == false secInfo.AddAllowedCertificate(new X509Certificate2(srv.peerCertFilePath)); // Add a CA certificate to check the certificate provided by the server - not required when ChainValidation == false secInfo.AddCA(new X509Certificate2(srv.rootCertFilePath)); // Check if the certificate is signed by a provided CA secInfo.ChainValidation = srv.chainValidation; // Check that the shown server certificate is in the list of allowed certificates secInfo.AllowOnlySpecificCertificates = srv.allowOnlySpecificCertificates; } catch (Exception e) { Log(srv.name + " - Error configuring TLS certficates."); Log(srv.name + " - " + e.Message); Environment.Exit(1); } } var tcpPort = 2404; string[] ipAddrPort = srv.ipAddresses[0].Split(':'); if (ipAddrPort.Length > 1) { if (int.TryParse(ipAddrPort[1], out _)) { tcpPort = System.Convert.ToInt32(ipAddrPort[1]); } } var con = new Connection(ipAddrPort[0], tcpPort, apcipars, alpars); con.Parameters.OA = srv.localLinkAddress; srv.conn1 = con; srv.conn2 = con; srv.connection = con; srv.CntGI = srv.giInterval - 3; srv.CntTestCommand = srv.testCommandInterval - 1; srv.CntTimeSync = 0; srv.CntTestCommandSeq = 0; if (LogLevel >= LogLevelDebug) { con.DebugOutput = true; } con.SetASDUReceivedHandler(AsduReceivedHandler, cntIecSrv); con.SetConnectionHandler(ConnectionHandler, cntIecSrv); if (srv.ipAddresses.Length > 1) // is there a secondary server ? { string[] ipAddrPort2 = srv.ipAddresses[1].Split(':'); if (ipAddrPort2.Length > 1) { if (int.TryParse(ipAddrPort2[1], out _)) { tcpPort = System.Convert.ToInt32(ipAddrPort2[1]); } } var c2 = new Connection(ipAddrPort2[0], tcpPort, apcipars, alpars); con.Parameters.OA = srv.localLinkAddress; srv.conn2 = c2; srv.connection = c2; // force initial swap to primary server if (LogLevel >= LogLevelDebug) { c2.DebugOutput = true; } c2.SetASDUReceivedHandler(AsduReceivedHandler, cntIecSrv); c2.SetConnectionHandler(ConnectionHandler, cntIecSrv); } if (srv.localCertFilePath != "" && secInfo != null) { srv.conn1.SetTlsSecurity(secInfo); srv.conn2.SetTlsSecurity(secInfo); } // create timer to increment counters each second srv.TimerCnt = new System.Timers.Timer(); srv.TimerCnt.Interval = 1000; srv.TimerCnt.Elapsed += (sender, e) => MyElapsedMethod(sender, e, srv);
public static void Main(string[] args) { bool running = true; Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; running = false; }; // Own certificate has to be a pfx file that contains the private key X509Certificate2 ownCertificate = new X509Certificate2("server.pfx"); // Create a new security information object to configure TLS TlsSecurityInformation secInfo = new TlsSecurityInformation(ownCertificate); // Add allowed client certificates - not required when AllowOnlySpecificCertificates == false secInfo.AddAllowedCertificate(new X509Certificate2("client1.cer")); secInfo.AddAllowedCertificate(new X509Certificate2("client2.cer")); // Add a CA certificate to check the certificate provided by the server - not required when ChainValidation == false secInfo.AddCA(new X509Certificate2("root.cer")); // Check if the certificate is signed by a provided CA secInfo.ChainValidation = true; // Check that the shown client certificate is in the list of allowed certificates secInfo.AllowOnlySpecificCertificates = true; // Use constructor with security information object, this will force the connection using TLS (using TCP port 19998) Server server = new Server(secInfo); server.DebugOutput = true; server.MaxQueueSize = 10; server.SetInterrogationHandler(interrogationHandler, null); server.SetASDUHandler(asduHandler, null); server.Start(); ASDU newAsdu = new ASDU(server.GetConnectionParameters(), CauseOfTransmission.INITIALIZED, false, false, 0, 1, false); EndOfInitialization eoi = new EndOfInitialization(0); newAsdu.AddInformationObject(eoi); server.EnqueueASDU(newAsdu); int waitTime = 1000; while (running) { Thread.Sleep(100); if (waitTime > 0) { waitTime -= 100; } else { newAsdu = new ASDU(server.GetConnectionParameters(), CauseOfTransmission.PERIODIC, false, false, 2, 1, false); newAsdu.AddInformationObject(new MeasuredValueScaled(110, -1, new QualityDescriptor())); server.EnqueueASDU(newAsdu); waitTime = 1000; } } Console.WriteLine("Stop server"); server.Stop();; }
static void Main(string[] args) { Log("{json:scada} IEC60870-5-104 Server Driver - Copyright 2020 RLO"); Log("Driver version " + DriverVersion); Log("Using lib60870.NET version " + LibraryCommon.GetLibraryVersionString()); if ( args.Length > 0 // first argument in number of the driver instance ) { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if ( args.Length > 1 // second argument is logLevel ) { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); // connect to MongoDB Database server Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <IEC10X_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (IEC10X_connection isrv in conns) { IEC10Xconns.Add(isrv); Log(isrv.name.ToString()); } if (IEC10Xconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to dequeue iec data and send to connections Thread thrDeqIecInfo = new Thread(() => DequeueIecInfo()); thrDeqIecInfo.Start(); // start thread to watch for commands in the database using a change stream Thread thrMongoCS = new Thread(() => ProcessMongoCS(JSConfig)); thrMongoCS.Start(); Log("Setting up IEC Connections & ASDU handlers..."); int cntIecSrv = 0; foreach (IEC10X_connection srv in IEC10Xconns) { var apcipars = new APCIParameters(); apcipars.K = srv.k; apcipars.W = srv.w; apcipars.T0 = srv.t0; apcipars.T1 = srv.t1; apcipars.T2 = srv.t2; apcipars.T3 = srv.t3; var alpars = new ApplicationLayerParameters(); alpars.SizeOfCOT = srv.sizeOfCOT; alpars.SizeOfCA = srv.sizeOfCA; alpars.SizeOfIOA = srv.sizeOfIOA; alpars.OA = srv.localLinkAddress; TlsSecurityInformation secInfo = null; if (srv.localCertFilePath != "") { try { // Own certificate has to be a pfx file that contains the private key X509Certificate2 ownCertificate = new X509Certificate2(srv.localCertFilePath, srv.passphrase, X509KeyStorageFlags.MachineKeySet); // Create a new security information object to configure TLS secInfo = new TlsSecurityInformation(null, ownCertificate); // Add allowed server certificates - not required when AllowOnlySpecificCertificates == false secInfo.AddAllowedCertificate(new X509Certificate2(srv.peerCertFilePath)); foreach (string peerCertFilePath in srv.peerCertFilesPaths) { secInfo.AddAllowedCertificate(new X509Certificate2(peerCertFilePath)); } // Add a CA certificate to check the certificate provided by the server - not required when ChainValidation == false secInfo.AddCA(new X509Certificate2(srv.rootCertFilePath)); // Check if the certificate is signed by a provided CA secInfo.ChainValidation = srv.chainValidation; // Check that the shown server certificate is in the list of allowed certificates secInfo.AllowOnlySpecificCertificates = srv.allowOnlySpecificCertificates; } catch (Exception e) { Log(srv.name + " - Error configuring TLS certificates."); Log(srv.name + " - " + e.Message); Environment.Exit(1); } } var server = new Server(apcipars, alpars, secInfo); srv.server = server; if (srv.serverModeMultiActive) { server.ServerMode = ServerMode.CONNECTION_IS_REDUNDANCY_GROUP; } else { server.ServerMode = ServerMode.SINGLE_REDUNDANCY_GROUP; } var localBindIpAddress = "0.0.0.0"; var tcpPort = 2404; string[] ipAddrPort = srv.ipAddressLocalBind.Split(':'); if (ipAddrPort.Length > 0) { if (ipAddrPort[0] != "") { localBindIpAddress = ipAddrPort[0]; } } if (ipAddrPort.Length > 1) { if (int.TryParse(ipAddrPort[1], out _)) { tcpPort = System.Convert.ToInt32(ipAddrPort[1]); } } server.SetLocalAddress(localBindIpAddress); server.SetLocalPort(tcpPort); //RedundancyGroup redGroup = new RedundancyGroup("catch all"); //server.AddRedundancyGroup(redGroup); if (LogLevel >= LogLevelDebug) { server.DebugOutput = true; } server.MaxQueueSize = srv.maxQueueSize; server.MaxOpenConnections = srv.maxClientConnections; Log(srv.name + " - Max Queue Size: " + server.MaxQueueSize); Log(srv.name + " - Max Client Connections: " + server.MaxOpenConnections); server.SetConnectionRequestHandler( ConnectionRequestHandler, cntIecSrv ); server.SetConnectionEventHandler( ConnectionEventHandler, cntIecSrv ); server.SetInterrogationHandler( InterrogationHandler, cntIecSrv ); server.SetASDUHandler(AsduReceivedHandler, cntIecSrv); server.Start(); Log(srv.name + " - New server listening on " + localBindIpAddress + ":" + tcpPort); cntIecSrv++; } Thread.Sleep(1000); bool running = true; Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; running = false; }; Log("Press [CTRL]+[C] to terminate..."); do { try { if (Client == null) { // retry connection Client = new MongoClient(JSConfig.mongoConnectionString); DB = Client.GetDatabase(JSConfig.mongoDatabaseName); } IsMongoLive = DB .RunCommandAsync((Command <BsonDocument>) "{ping:1}") .Wait(10000); if (!IsMongoLive) { throw new Exception("Error on MongoDB connection "); } //foreach (IEC104_connection srv in IEC10Xconns) //{ //} } catch (Exception e) { // Disconnects to retry after some time Client = null; Log("Exception Mongo"); Log(e); Log(e .ToString() .Substring(0, e.ToString().IndexOf(Environment.NewLine))); System.Threading.Thread.Sleep(3000); } Thread.Sleep(1000); }while (running); Log("Exiting application!"); Environment.Exit(0); /* Synchronize clock of the controlled station */ //con.SendClockSyncCommand(1 /* CA */, new CP56Time2a(DateTime.Now)); }