Getting Started with IPWorks DTLS
Requirements: IPWorks DTLS
Introduction
IPWorks DTLS provides a simple, easy-to-use, and inuitive Datagram Transport Layer Security (DTLS) client server and library. Developers can quickly add client and server support for DTLS connections to easily exchange data between parties.
Contents
- DTLS Server
- Getting Started
- Handling Incoming Connections
- Sending and Receiving Data
- Removing Connections
- Additional Information
- DTLS Client
DTLS Server
The DTLSServer component functions as a server that facilities incoming DTLS connections and offers a convenient means of transmitting and receiving datagrams over the established secure connections.
Getting Started
First, a valid certificate must be selected before the server can start listening for incoming connections. The certificate can be specified with the SSLCert property. Note that the certificate must contain a private key.
After specifying the certificate, calling the StartListening method will activate the component and start listening for incoming connections. The component will listen on the interface identified by the LocalHost and LocalPort properties specified. Otherwise, these values will be set by the component. If applicable, these values must be set before calling StartListening. For example:
//dtlsserver.LocalHost = "some_ip_address";
//dtlsserver.LocalPort = 1234;
dtlsserver.SSLCert = new Certificate("/path/to/cert.pfx", CertStoreTypes.cstPFXFile, "cert_password", "cert_subject");
dtlsserver.StartListening();
Console.WriteLine("Listening on: " + dtlsserver.LocalHost + ":" + dtlsserver.LocalPort);
while (dtlsserver.Listening) {
dtlsserver.DoEvents();
}
Handling Incoming Connections
Once successfully listening, the component can now handle and accept (or reject) incoming connections. The first indicator of an incoming connection will be through the ConnectionRequest event. Here, the connection's originating address and port can be queried. By default, the component will accept all incoming connections, but this behavior can be overridden within this event.
Assuming the connection is accepted, the DTLS handshake will proceed. Relevant handshake details will be reported by the SSLStatus event. By default, the client is not required to present a certificate to the server. To force this, the AuthenticateClients property can be enabled. When enabled, the client's presented certificate will be available within the SSLClientAuthentication event, where the server can again choose to accept (or reject) the connection.
Once the connection is complete (or fails), the Connected event will fire. Note that this event will fire if a connection succeeds or fails. If successful, the event will fire with a StatusCode of 0. If this value is non-zero, it indicates the connection was unsuccessful. In this case, the Description parameter will contain relevant details.
After a successful connection, relevant connection-specific details will be available within the Connections collection. Each connection will be assigned a unique ConnectionId, which can be acquired for a given connection within the Connected event. For example:
dtlsserver.OnConnected += (o, e) => {
if (e.StatusCode == 0) {
Console.WriteLine("Successful connection from " + e.SourceAddr + ":" + e.SourcePort);
Console.WriteLine("ConnectionId: " + e.ConnectionId);
} else {
Console.WriteLine("Connection failed from " + e.SourceAddr + ":" + e.SourcePort);
Console.WriteLine("Error code: " + e.StatusCode);
Console.WriteLine("Error description: " + e.Description);
}
};
dtlsserver.OnSSLClientAuthentication += (o, e) => {
if (e.Accept) return;
Console.Write("Client provided the following certificate:\nIssuer: " + e.CertIssuer + "\nSubject: " + e.CertSubject + "\n");
Console.Write("The following problems have been determined for this certificate: " + e.Status + "\n");
Console.Write("Would you like to accept anyways? [y/n] ");
if (Console.Read() == 'y') e.Accept = true;
};
dtlsserver.SSLCert = new Certificate("/path/to/cert.pfx", CertStoreTypes.cstPFXFile, "cert_password", "cert_subject");
dtlsserver.AuthenticateClients = true;
dtlsserver.StartListening();
Console.WriteLine("Listening on: " + dtlsserver.LocalHost + ":" + dtlsserver.LocalPort);
while (dtlsserver.Listening) {
dtlsserver.DoEvents();
}
Sending and Receiving Data
The component can send data to individual connections, specified by the ConnectionId parameter, with the SendBytes and SendText methods.
While a connection is active, incoming data from a connection will be made available within the DataIn event. Note that this event is non-reentrant, and it is recommended to offload time-consuming operations to ensure the best performance.
If required, the PauseData method can be called, disabling the reception of incoming data from a particular connection. Data reception can later be enabled by calling the ProcessData method. Note that if this reception is disabled for a connection, the connection may continue sending data, which will remain unprocessed by the component. In this case, the underlying socket buffer may be filled. This can result in possible data loss originating from this connection. Please use these methods with caution.
This may look like the following:
dtlsserver.OnDataIn += (o, e) => {
Console.WriteLine("Packet received from: " + e.ConnectionId);
Console.WriteLine("Packet: " + e.Datagram);
};
dtlsserver.SSLCert = new Certificate("/path/to/cert.pfx", CertStoreTypes.cstPFXFile, "cert_password", "cert_subject");
dtlsserver.StartListening();
Console.WriteLine("Listening on: " + dtlsserver.LocalHost + ":" + dtlsserver.LocalPort);
...
...
...
// Broadcast data
foreach (DTLSConnection c in dtlsserver.Connections.Values) {
dtlsserver.SendText(c.ConnectionId, "Hello world!");
}
Removing Connections
To remove a connection, the Disconnect method must be called with the associated ConnectionId. In order to automatically remove inactive connections, the DefaultIdleTimeout property can be set accordingly. By default, this property is set to 0 and idle connections are not removed automatically. When this property is set to a positive value this will automatically remove connections that are idle for a specified time.
Once a connection ends, the Disconnected event will fire. In the case a connection ends and an error is encountered, the StatusCode and Description parameters will contain relevant details regarding the error. The connection will then be removed from the Connections collection. For example:
dtlsserver.OnDisconnected += (o, e) => {
if (e.StatusCode == 0) {
Console.WriteLine("Connection removed: " + e.ConnectionId);
} else {
Console.WriteLine("Connection removed: " + e.ConnectionId);
Console.WriteLine("Error code: " + e.StatusCode);
Console.WriteLine("Error description: " + e.Description);
}
};
dtlsserver.DefaultIdleTimeout = 60; // Remove connections inactive for 60 seconds
dtlsserver.StartListening();
Console.WriteLine("Listening on: " + dtlsserver.LocalHost + ":" + dtlsserver.LocalPort);
while (dtlsserver.Listening) {
dtlsserver.DoEvents();
}
Additional Information
To support the functionality exposed by the DefaultIdleTimeout and KeepAlive properties, it is important to note that the DoEvents method must be called regularly in both console and form-based applications.
For DefaultIdleTimeout, DoEvents must be called frequently to ensure that idle connections are handled and removed in a timely manner. For KeepAlive, DoEvents must be called frequently to ensure the component send keep-alive (or Heartbeat) packets to existing connections in a timely manner.
DTLS Client
The DTLSClient component functions as a client that facilitates in connecting to DTLS servers and offers a convenient means of transmitting and receiving datagrams over the established, secure connection.
Getting Started
First, the RemoteHost and RemotePort properties must be set to the address and port of the target DTLS server. The Timeout property can be used to specify a timeout when connecting to the server. To initiate the connection, call the Connect method.
After doing so, the handshake process will begin. Relevant handshake details will be reported by the SSLStatus event. Initially, the server will present its certificate to the client. The certificate can be evaluated within the SSLServerAuthentication event. By default, the component will provide a recommendation of whether to accept or reject the certificate and reasoning behind the recommendation. Regardless, the certificate can be manually accepted (or rejected) within this event.
In some cases, the server requires the client to present a certificate as well. In this case, a valid certificate will need to be specified with the SSLCert property.
Once the connection is complete (or fails), the Connected event will fire. Note that this event will fire if a connection succeeds or fails. If successful, the event will fire with a StatusCode of 0. If this value is non-zero, it indicates the connection was unsuccessful. The Description parameter will contain relevant details. Upon successful connection, the Connected property will be set to True. This process may look like the following:
dtlsclient.OnConnected += (o, e) => {
if (e.StatusCode == 0) {
Console.WriteLine("Connection successful!");
} else {
Console.WriteLine("Connection failed.");
Console.WriteLine("Error code: " + e.StatusCode);
Console.WriteLine("Error description: " + e.Description);
}
};
dtlsclient.OnSSLServerAuthentication += (o, e) => {
if (e.Accept) return;
Console.Write("Server provided the following certificate:\nIssuer: " + e.CertIssuer + "\nSubject: " + e.CertSubject + "\n");
Console.Write("The following problems have been determined for this certificate: " + e.Status + "\n");
Console.Write("Would you like to accept anyways? [y/n] ");
if (Console.Read() == 'y') e.Accept = true;
};
dtlsclient.RemoteHost = "remote_ip";
dtlsclient.RemotePort = 1234;
dtlsclient.Timeout = 30;
// if client authentication is applicable
dtlsclient.SSLCert = new Certificate("/path/to/cert.pfx", CertStoreTypes.cstPFXFile, "cert_password", "cert_subject");
dtlsclient.Connect();
Sending and Receiving Data
Once a successful connection is established, the component can send data to the server with the SendText and SendBytes methods.
When data is received from the server the DataIn event will fire. Note that this event is non-reentrant. It is recommended to offload time-consuming operations to ensure the best performance.
If required, the PauseData method can be called, disabling the reception of incoming data from the server. Data reception can later be enabled by calling the ProcessData method. Note that if this reception is disabled, the server may continue sending data, which will remain unprocessed by the component. In this case, the underlying socket buffer may be filled. This can result in possible data loss originating from the server. Please use these methods with caution.
This may look like the following:
dtlsclient.OnSSLServerAuthentication += (o, e) => {
e.Accept = true;
};
dtlsclient.OnDataIn += (o, e) => {
Console.WriteLine("Packet received from server.");
Console.WriteLine("Datagram: " + e.Datagram);
}
dtlsclient.RemoteHost = "remote_ip";
dtlsclient.RemotePort = 1234;
dtlsclient.Connect();
dtlsclient.SendText("Hello World!");
while (true) {
dtlsclient.DoEvents();
}
Disconnecting from a Server
Once the connection to the server is broken, the Disconnected event will fire. The disconnection can be performed by calling the Disconnect method, or performed by the server.
In the case a connection ends and an error is encountered, the StatusCode and Description parameters will contain relevant details regarding the error. For example:
dtlsclient.OnDisconnected += (o, e) => {
if (e.StatusCode == 0) {
Console.WriteLine("Connection ended.");
} else {
Console.WriteLine("Connection ended.");
Console.WriteLine("Error code: " + e.StatusCode);
Console.WriteLine("Error description: " + e.Description);
}
};
dtlsclient.RemoteHost = "remote_ip";
dtlsclient.RemotePort = 1234;
dtlsclient.Connect();
...
...
...
dtlsclient.Disconnect();
Additional Information
To support KeepAlive functionality, it is important to note that the DoEvents method must be called regularly in both console and form-based applications. This is to ensure the component sends keep-alive (or Heartbeat) packets to the server in a timely manner.
We appreciate your feedback. If you have any questions, comments, or suggestions about this article please contact our support team at kb@nsoftware.com.