Best Practice to creating an custom expandable protocol architecture? [closed] - protocols

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
Disclaimer: I have 0 experience regarding creating custom, larger scaled protocols.
I am about to start a new project for fun (preferably in java), consisting of one Master-Server (MS), several smaller servers (SS) on the same network, and several clients. All of those three parties should be to communicate information to each other.
Examples:
A Client 'logs in' to the MS.
The MS sends a client to an SS. (SS has to be started, MS sends IP/PORT of SS to Client and tells him to connect, SS waiting for Client to connect, ...)
SS and Client communicate information to each other (e.g. game server and client)
The most experience with custom protocols and packets on a larger scale I have is from Minecraft Servers (Spigot, etc.). When reading the Servers packet system I still get confused a bit.
Whilst researching this most of the time I only found basic tutorials on how to create a TCP/UDP Server-Client model in various programming languages, which I am not interested in.
What I want to Know:
I want to create my own protocol 'architecture', but I have no idea where to start. I want it to be very expandable, but not to complex.
Are there any common practices to creating good packets -> "How should a packet message look like?"
A simple answer or a link recommendation could already help me quite a lot! I know it is a very broad question, however I need to start at some point.

Basically what you are describing is a proxy server.
For now, this is what has come to my mind. Let me know any doubts so that I can solve them by expanding the response.
What is a proxy server?
A proxy server is a server that routes incoming traffic to other servers (internal or external) and acts as an intermediary between the client and the final server.
There are multiple approaches to your problem.
Approach 1: Nginx + JSON
In this case, I would recommend you use some proxy server like Nginx that uses the HTTP protocol. The information would then be transferred as JSON strings instead of using raw binary packets which would simplify the problem quite much.
For more info about NGINX:
Main website
Official docs
Nice youtube tutorial series for beginners.
For more info about JSON:
Working with JSON. Nice introduction by Mozilla.
Working with JSON in java with Jackson.
Approach 2: Making your own proxy server and using binary packets
For the proxy part, you could use Java Sockets and a class that distributes the connections by reading and opening packet form the client where it specifies the wanted destination. Then you would have two options:
Redirect the (Client-Proxy) socket streams to the (Proxy-WantedDestination) socket.
Tell the WantedDestination to open a connection to the client. (ServerSocket on client and Socket on WantedDestination) So in this way, the WantedDestination would open a socket connection with the Client instead of the Client opening a connection with the Wanted destination.
The first method allows you to log all incoming and outgoing data. The second method allows you to keep the WantedDestination secure.
First method:
Client <--> Proxy <--> WantedDestination (2 Sockets)
Second method:
Step 1: Client <--> Proxy
Step 2: Proxy <--> WantedDestination
Step 3: Client <---------------> WantedDestination (1 socket)
How to structure packets
I usually structure packets in the following way:
Packet header
Packet length
Packet payload
Packet checksum
The packet header can be used to identify if the packet is coming from your software and that you are starting to read the data from the right position.
The packet length will indicate how many bytes the stream must read before trying to deserialize the packet into its wrapper class. Let's imagine that the header has a length of 2 bytes and that the length has a length of 3 bytes. Then if the length indicates that the packet is 30 bytes long, you will know that the end of the packet is (30 - 3 - 2) = 25 bytes away.
The packet payload will have a variable size and will contain some fixed size bytes at the beginning indicating the packet type. The packet type can be chosen arbitrarily. For example, you can determine that a packet of type (byte) 12 must be interpreted as a packet containing data about a pong match.
Finally, the packet checksum indicates the sum of the bytes of the packet you that you can verify the integrity of the packet. Java already provides some checksum algorithms, such as CRC32. If Packet Checksum = CRC32(Packet header, Packet length, and Packet Payload), then the data is not corrupted.
In the end, a packet is a byte array that can be transmitted using Java Input and Output streams. Despite this, working directly with byte arrays can be usually difficult and frustrating, so I would recommend that you use a wrapper class to represent a packet and then extend that class to create other packets. For example:
package me.PauMAVA.DBAR.common.protocol;
import java.util.Arrays;
import java.util.zip.CRC32;
import java.util.zip.Checksum;
import static me.PauMAVA.DBAR.common.util.ConversionUtils.*;
public abstract class Packet implements Serializable {
public static final byte[] DEFAULT_HEADER = new byte[]{(byte) 0xAB, (byte) 0xBA};
private final byte[] header;
private final byte packetType;
private byte[] packetParameter;
private byte[] packetData;
private byte[] packetCheckSum;
Packet(PacketType type, PacketParameter parameter) {
this(type, parameter, new byte[0]);
}
Packet(PacketType type, PacketParameter parameter, byte[] data) {
this.header = DEFAULT_HEADER;
this.packetType = type.getCode();
this.packetParameter = parameter.getData();
this.packetData = data;
recalculateChecksum();
}
public byte[] getParameterBytes() {
return packetParameter;
}
public PacketParameter getPacketParameter() {
return PacketParameter.getByData(packetParameter);
}
public byte[] getPacketData() {
return packetData;
}
public void setParameter(PacketParameter parameter) {
this.packetParameter = parameter.getData();
recalculateChecksum();
}
public void setPacketData(byte[] packetData) {
this.packetData = packetData;
recalculateChecksum();
}
public void recalculateChecksum() {
Checksum checksum = new CRC32();
checksum.update(header);
checksum.update(packetParameter);
checksum.update(packetType);
if (packetData.length > 0) {
checksum.update(packetData);
}
this.packetCheckSum = longToBytes(checksum.getValue());
}
public byte[] toByteArray() {
return concatArrays(header, new byte[]{packetType}, packetParameter, packetData, packetCheckSum);
}
And then a custom packet could be:
package me.PauMAVA.DBAR.common.protocol;
import java.nio.charset.StandardCharsets;
import static me.PauMAVA.DBAR.common.util.ConversionUtils.subArray;
public class PacketSendPassword extends Packet {
private String passwordHash;
public PacketSendPassword() {
super(PacketType.SEND_PASSWORD, PacketParameter.NO_PARAM);
}
public PacketSendPassword(String passwordHash) {
super(PacketType.SEND_PASSWORD, PacketParameter.NO_PARAM);
super.setPacketData(passwordHash.getBytes(StandardCharsets.UTF_8));
}
#Override
public byte[] serialize() {
return toByteArray();
}
#Override
public void deserialize(byte[] data) throws ProtocolException {
validate(data, PacketType.SEND_PASSWORD, PacketParameter.NO_PARAM);
PacketParameter packetParameter = PacketParameter.getByData(subArray(data, 3, 6));
if (packetParameter != null) {
super.setParameter(packetParameter);
}
byte[] passwordHash = subArray(data, 7, data.length - 9);
super.setPacketData(passwordHash);
this.passwordHash = new String(passwordHash, StandardCharsets.UTF_8);
}
public String getPasswordHash() {
return passwordHash;
}
}
Sending a packet over a stream would be as easy as:
byte[] buffer = packet.serialize();
dout.write(buffer);
You can take a look at a small protocol that I developed for a Bukkit server auto reloader here.
Be advised that this method will need you to convert between different data types and byte arrays, so you would need a good understanding of numeric and character representation in binary.

Related

Reading TCP stream messages

I'm stuck writing a service that simply receives TCP stream messages using spring-integration. I'm using this class to send test message:
class TCPClient {
public static void main(String args[]) throws Exception {
Socket clientSocket = new Socket("localhost", 9999);
clientSocket.getOutputStream().write("XYZ".getBytes());
clientSocket.close();
}
}
Server code, that should receive a message:
#EnableIntegration
#IntegrationComponentScan
#Configuration
public class TcpServer {
#Bean
public AbstractServerConnectionFactory serverCF() {
return new TcpNetServerConnectionFactory(9999);
}
#Bean
public TcpInboundGateway tcpInGate(AbstractServerConnectionFactory conFactory) {
TcpInboundGateway inGate = new TcpInboundGateway();
inGate.setConnectionFactory(conFactory);
SubscribableChannel channel = new DirectChannel();
//Planning to set custom message handler here, to process messages later
channel.subscribe(message -> System.out.println(convertMessage(message)));
inGate.setRequestChannel(channel);
return inGate;
}
private String convertMessage(Message<?> message) {
return message == null || message.getPayload() == null
? null
: new String((byte[]) message.getPayload());
}
}
Problem: when client code is run - server logs the following exception:
TcpNetConnection : Read exception localhost:46924:9999:6d00ac25-b5c8-47ac-9bdd-edb6bc09fe55 IOException:Socket closed during message assembly
A am able to receive a message when I send it using telnet or when I use simple java-only tcp-server implementation. How can I configure spring-integration to be able to read message sent from client?
The default deserializer expects messages to be terminated with CRLF (which is what Telnet sends, and why that works).
Send "XYZ\r\n".getBytes().
Or, change the deserializer to use a ByteArrayRawDeserializer which uses the socket close to terminate the message.
See the documentation about (de)serializers here.
TCP is a streaming protocol; this means that some structure has to be provided to data transported over TCP, so the receiver can demarcate the data into discrete messages. Connection factories are configured to use (de)serializers to convert between the message payload and the bits that are sent over TCP. This is accomplished by providing a deserializer and serializer for inbound and outbound messages respectively. A number of standard (de)serializers are provided.
The ByteArrayCrlfSerializer*, converts a byte array to a stream of bytes followed by carriage return and linefeed characters (\r\n). This is the default (de)serializer and can be used with telnet as a client, for example.
...
The ByteArrayRawSerializer*, converts a byte array to a stream of bytes and adds no additional message demarcation data; with this (de)serializer, the end of a message is indicated by the client closing the socket in an orderly fashion. When using this serializer, message reception will hang until the client closes the socket, or a timeout occurs; a timeout will NOT result in a message.

Java Card applets, secure data transmission and Secure Channel

I want to write my applet in a way that its APDU commands and status words wasn't be clear in the transmission channel between my card and my reader. I mean I don't want to send APDU commands and responses to be plain text for third parties.
I think I have two option :
After selecting my applet on the card, for all the other commands, do an encryption function on the data section of APDU commands and decrypt them on the card and after that analyze them. Note that I can't encrypt whole the command using this methodology because the result may have conflict with another SELECT APDU command and the SD of card recognize it as a SELECT command wrongly. is that right?
Its Diagram :
Using SD Secure Channel : As far as I know secure channel means : Whole of the APDU commands and responses transmit in an encrypted form (i.e. they encrypt in the source(Security Domain/Card reader) and decrypt in the destination(Secutity Domain/Card Reader). is that right? As far as I know the SD perform the cryptography method role in this mechanism and the communication between my applet and the SD is in plain (Below diagram), right?
Its Diagram :
Is there any other way?
It seems that the first solution is not good enough, because :
I must implement it myself! :)
We can't hide all parts of the command and response from third-parties.(We can hide data only)
Am I right?
Now, let assume that I want to be sure that my applet works only with APDU commands that transmitted using secure channel . I think I have two option again :
Put the card in SECURED state. As the user can't communicate with the card with plain text APDU commands in this state (right?) therefore he must send the commands to my applet using secure channel. right? if it is not correct, is there any way to force the SD to work with Secure Channel only?
Keep the card in any life cycle that it is (for example OP_READY), But instead, on reception of any APDU command, check the CLA section to see if it is a secure transmitted or not! (Is it possible? Is there any difference between CLA part of APDU commands that come from secure channel and the other ones? Am I right?)
Is there any other way?
And finally the main question :
How can I use SD to have a secure communication with my applet? As I thought I must use GlobalPlatform classes(Am I?), I took a look at its API-s. I found a method named getSecureChannel in a package named org.globalplatform.GPSystem. Am I in a right way? Am I must use this method?
I know that this may be too long to answer, but I'm sure that it clarify a lot of questions not only for me, but also for other future viewers.
I appreciate any body shed any light in this issue for me.
And a sample applet is more appreciable.
Don't worry for secure channel communication via applet. It's very simple if you use Global Platform APIs in your applet.
You don't need to think about lot's of questions, just try to write an secure channel applet and it will process your applet as per defined security level in the command data.
Refer GP secure channel APIs:
http://www.win.tue.nl/pinpasjc/docs/apis/gp22/
And you should keep the card in SECURED state.
And this is the sample applet for secure channel scp02:
package secureChannel;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import org.globalplatform.GPSystem;
import org.globalplatform.SecureChannel;
public class Scp02 extends Applet
{
final static byte INIT_UPDATE = (byte) 0x50;
final static byte EXT_AUTHENTICATE = (byte) 0x82;
final static byte STORE_DATA = (byte) 0xE2;
public static void install(byte[] bArray, short sOffset, byte bLength)
{
new Scp02().register(bArray, sOffset, bLength);
}
public void process(APDU apdu) throws ISOException
{
SecureChannel sc = GPSystem.getSecureChannel();
byte[] buffer = apdu.getBuffer();
short inlength = 0;
switch (ISO7816.OFFSET_INS)
{
case INIT_UPDATE:
case EXT_AUTHENTICATE:
inlength = apdu.setIncomingAndReceive();
sc.processSecurity(apdu);
break;
case STORE_DATA:
//Receive command data
inlength = apdu.setIncomingAndReceive();
inlength = sc.unwrap(buffer, (short) 0, inlength);
apdu.setOutgoingAndSend((short)0, inlength);
//Process data
break;
}
}
}
I'll answer in order:
Yes, for ISO/IEC 7816-4 only the data section is encrypted. The header is just protected by the authentication tag.
No, the Global Platform secure channel also just (optionally) encrypts the data. The integrity is over header and command data though.
No, the secured state is for Global Platform only, you'll have to program this for yourself using the on card GP API. The GP API has access methods to perform authentication, request the secure channel and to retrieve the current state.
Correct, the CLA byte determines if the APDU is encrypted (not how it is encrypted though). If the first bit of the CLA is zero then your secure channel must however be compliant to ISO/IEC 7816-4.
For the sake of Google search, the code from Anurag Bajpai doesn't work without a slight modification since, as stated in GP secure channel APIs, the applet should output eventual response data:
If response data is present, this data will be placed in the APDU buffer at offset ISO7816.OFFSET_CDATA. The return value indicates the length and the applet is responsible for outputting this data if necessary.
Hence, the corrected code is:
package secureChannel;
import javacard.framework.APDU;
import javacard.framework.Applet;
import javacard.framework.ISO7816;
import javacard.framework.ISOException;
import org.globalplatform.GPSystem;
import org.globalplatform.SecureChannel;
public class Scp02 extends Applet
{
final static byte INIT_UPDATE = (byte) 0x50;
final static byte EXT_AUTHENTICATE = (byte) 0x82;
final static byte STORE_DATA = (byte) 0xE2;
public static void install(byte[] bArray, short sOffset, byte bLength)
{
new Scp02().register(bArray, sOffset, bLength);
}
public void process(APDU apdu) throws ISOException
{
SecureChannel sc = GPSystem.getSecureChannel();
byte[] buffer = apdu.getBuffer();
short inlength = 0;
switch (ISO7816.OFFSET_INS)
{
case INIT_UPDATE:
case EXT_AUTHENTICATE:
inlength = apdu.setIncomingAndReceive();
short respLen = sc.processSecurity(apdu);
apdu.setOutgoingAndSend(ISO7816.OFFSET_CDATA, respLen);
break;
case STORE_DATA:
//Receive command data
inlength = apdu.setIncomingAndReceive();
inlength = sc.unwrap(buffer, (short) 0, inlength);
apdu.setOutgoingAndSend((short)0, inlength);
//Process data
break;
}
}
}
Please note that it is incorrect to call apdu.setIncomingAndReceive() before calling sc.processSecurity(apdu), as processSecurity() "is responsible for receiving the data field of commands that are recognized".

gwt AsyncCallback - java

I'm doing an asynchronous call to a servlet. In the servlet I am taking some pictures from DB and sending them back to client as Strings. In the Client..in onSuccess(String pictureAsString) method I need to do something with the images after they are loaded and BEFORE going any further. I know the number of images and currently I'm doing some testing like this:
public void onSuccess(String result) {
numberOfReceivedImages = numberOfReceivedImages-1;
//add image as string to a list of images
if(numberOfReceivedImages == 0){
////call method that draws the images from the list }
}
The order in wich the images are drawn should be the order from the list in which the images are stored after receiving them from server, but this order is not kept...and I suppose this is because I'm not receiving all the images by the time I'm drawing them.
Thank you,
any help is appreciated
Are you sure the images are sent in the right order from the server? How do you store them client-side?
I would suggest that you change the signature of your RPC method to somthing like
AsyncCallback<List<String>> callback = new AsyncCallback()
{
public void onSuccess( List<String> result ) {
...
}
...
}
and of course relevant changes on server side. That way, you're guaranteed the same ordering client side as on server side.

How to broadcast Video using UDPCLient Class in C# over internet?

I am trying to develop a Video Client/functionality that captures video using webcam and transfers to other servent (server-client) somewhere on the internet. I am using UDPCLient Class to do that.
I want my application to be able to listen and tarnsmit video captured from webcam. The capturing, transmission and receiving works fine when i do that on local network.
But when i test the application from behind router (across two differnt networks/internet) after forwarding respective ports, the internet connectivity is lost on both routers (They hang up or something) and i need to restart the routers or switch to an alternate connection. The configuration is as follows:
Servent 1 <--> Router1 <--> Internet Connection#01
Servent 02 <---> Router2 <---> Internet Connection#02
Both connections are on separate DSL Line. One of the routers is ZTE brand and the other is of Netgear.
Code for listenning/transmission is as follows:
private void StartSockets()
{
//For testing across internet i use IPAddress obtained via different function
var IPAddress = getMyIpAddress();
this.udpSender = new UdpClient(IpAddress, 4000);
this.udpListener = new UdpClient(4000);
}
private IPAddress getMyIpAddress()
{
IPAddress localIP ;//= AddressAr[0];
localIP = IPAddress.Parse(GetPublicIP());
return localIP;
}
public string GetPublicIP()
{
String direction = "";
WebRequest request = WebRequest.Create("http://checkip.dyndns.org/");
using (WebResponse response = request.GetResponse())
{
using (StreamReader stream = new StreamReader(response.GetResponseStream()))
{
direction = stream.ReadToEnd();
}
}
//Search for the ip in the html
int first = direction.IndexOf("Address: ") + 9;
int last = direction.LastIndexOf("</body>");
direction = direction.Substring(first, last - first);
return direction;
}
Code for receiving response is as follows:
private void ReceiveData()
{
//For testing across internet i use IPAddress obtained via different function
var IPAddress = getMyIpAddress();
IPEndPoint ep = new IPEndPoint(IPAddress, myPort);
try
{
byte[] receiveBytes = this.udpListener.Receive(ref ep);
this.OnReadImage(new ImageEventArgs(this.ByteToImage(receiveBytes)));
}
catch (Exception)
{
}
}
If i test on local network , i use DNSHostname to get ip address (private ip addresses) and video works fine on local network. That does not work over internet so i switch to live Ip Address and thus i use the method of getPublicIpAddress().
I know there is something seriously wrong with my approach? What would be right approach?
Should i switch to TCP Listenner? I intend to have multiple receiver of same video in future. So would that affect?
Can UDP clients cause routers to crash, hang up and restart? How can i avoid that?
Lastly, if were to avoid port-forwarding what would be the best strategy?
Please help.
Thanks
Steve

J2ME Audio Streaming through SIP Connection

I am creating a J2ME real time streaming audio player with RTP and through SIP connection. Also I am new for these things. I want to take look deeply those things. If any one know a good working sample code demonstrating an audio player streaming with RTP (That means how to send a REGISTER message to the server through SIP to get registered and send an INVITE message and get the response & play). Please let me know, highly appreciated.
Also I looked here
if
My server port is 6060
ip 111.111.111.1
id is myid password 123
Have I used the code correctly? If I am wrong, please make me correct.
public void doRegister(String username, String password, String realm) {
SipClientConnection scc = null;
SipConnectionNotifier scn = null;
String contact = null;
try {
scn = (SipConnectionNotifier) Connector.open("sip:5080");
contact = new String("sip:myid:123#"+scn.getLocalAddress()+":"+scn.getLocalPort());
scc = (SipClientConnection) Connector.open("sip:111.111.111.1+"transport=tcp") ;
scc.initRequest("REGISTER", scn);
scc.setHeader("From", "sip:myid:123#"+scn.getLocalAddress()+":5080");
scc.setHeader("To", "sip:myid:123#111.111.111.1");
scc.setHeader("Contact", contact);
scc.send();
boolean handled = false;
int scode = 0;
while(!handled) {
SipHeader sh;
scc.receive(30000);
scode = scc.getStatusCode();
switch(scode){
case 401:
sh = new SipHeader("WWW-Authenticate",
scc.getHeader("WWW-Authenticate"));
realm = sh.getParameter("realm");
scc.setCredentials(username, password, realm);
break;
case 407:
sh = new SipHeader("Proxy-Authenticate",
scc.getHeader("Proxy-Authenticate"));
realm = sh.getParameter("realm");
scc.setCredentials(username, password, realm);
break;
case 200:
handled = true;
break;
default:
handled = true;
}
}
scc.close();
} catch(Exception ex) {
// handle Exceptions
}
}
I got a respond with 180 Rigging message. Also let me know what is realm here. scc.setCredentials(username, password, realm);
As you see here in example 1 - you realize that when you make a fresh Reqeust to server, where as server expects authentication it first sends 401. By seeing this the client can then either search for a password or ask the user. When server sends the 401 response code, it specifies which security domain is applicable for the given requests. This is already what you have got in your code :
realm = sh.getParameter("realm");
Once, failed, you need to send() the request again with credentials here. I guess the setCredentials() function is only setting these parameters inside the scc object and they will be applied when send() is called again.
Some references that might be of interest: http://www.developer.nokia.com/Community/Discussion/showthread.php?126760-SIP-registration-401-Unauthorized-..
(here people had issues related port number, which i am not sure if this is bothering you)
Many functions and more things are available and wide answer can be found here Also Nokia JSR180 API has sample codes as well

Resources