Showing posts with label network-programming. Show all posts
Showing posts with label network-programming. Show all posts

Thursday, April 21, 2016

How to implement nested protocols with boost::asio?

Leave a Comment

I'm trying to write a server that handles protocol A over protocol B.

Protocol A is HTTP or RTSP, and protocol B is a simple sequence of binary packets:

[packet length][...encrypted packet data...] 

So I want to use things like that:

boost::asio::async_read_until(socket, inputBuffer, "\r\n\r\n", read_handler); 

However, instead of socket use some pseudo-socket connected to Protocol B handlers.

I have some ideas:

  1. Forget about async_read, async_read_until, etc., and write two state machines for A and B.

  2. Hybrid approach: async_read_* for protocol B, state machine for A.

  3. Make internal proxy server.

I don't like (1) and (2) because

  • It's hard to decouple A from B (I want to be able to disable protocol B).

  • Ugly.

(3) just looks ugly :-)

So the question is: how do I implement this?

2 Answers

Answers 1

I won't go over boost::asio, since this seems more a design pattern than a networking one. I'd use the State Pattern. This way you could change protocol on the fly.

class net_protocol { protected:     socket sock;  public:     net_protocol(socket _sock) : sock(_sock) {}      virtual net_protocol* read(Result& r) = 0; };  class http_protocol : public net_protocol { public:     http_protocol(socket _sock) : net_protocol(_sock) {}      net_protocol* read(Result& r) {         boost::asio::async_read_until(socket, inputBuffer, "\r\n\r\n", read_handler);         // set result, or have read_handler set it         return this;     } };  class binary_protocol : public net_protocol { public:     binary_protocol(socket _sock) : net_protocol(_sock) {}      net_protocol* read(Result& r) {         // read 4 bytes as int size and then size bytes in a buffer. using boost::asio::async_read         // set result, or have read_handler set it          // change strategy example         //if (change_strategy)         //  return new http_strategy(sock);          return this;     } }; 

You'd initialize the starting protocol with

std::unique_ptr<net_protocol> proto(new http_protocol(sock)); 

then you'd read with:

//Result result; proto.reset(proto->read(result)); 

EDIT: the if() return new stragegy are, in fact, a state machine

if you are concerned about those async reads and thus can't decice which return policies, have the policy classes call a notify method in their read_handler

class caller {     std::unique_ptr<net_protocol> protocol;     boost::mutex io_mutex;  public:     void notify_new_strategy(const net_protocol* p) {          boost::unique_lock<boost::mutex> scoped_lock(mutex);         protocol.reset(p);     }      void notify_new_result(const Result r) { ... } }; 

If you don't need to change used protocol on the fly you would have no need of State, thus read() would return Result (or, void and call caller::notify_new_result(const Result) if async). Still you could use the same approach (2 concrete classes and a virtual one) and it would probably be something very close to Strategy Pattern

Answers 2

I have done something like your answer (2) in the past - using async_read calls to read the header first and then another async_read to read the length and forward the remaining things to a hand written state machine. But I wouldn't necessarily recommend that to you - You thereby might get zero-copy IO for protocol B but doing an IO call reading the 4-8 byte header is quite wasteful when you know there is always data coming behind it. And the problem is that your network abstraction for the 2 layers will be different - so the decoupling problem that you mention really exists.

Using a fixed length buffer, only calling async_read and then processing the data with 2 nested state machines (like you are basically proposing in answer (1)) works quite well. Your state machine for each would simple get pushed some new received data (from either directly the socket or from the lower state machine) and process that. This means A would not be coupled to B here, as you could directly push the data to the A state machine from asio, if the input/output data format matches.

Similar to this are the patterns that are used in the Netty and Facebook Wangle libraries, where you have handlers that get data pushed from a lower handler in the pipeline, perform their actions based on that input and output their decoded data to the next handler. These handlers can be state machines, but depending on the complexity of the protocol don't necessarily have to be. You can take some inspiration from that, e.g. look at some Wangle docs: https://github.com/facebook/wangle/blob/master/tutorial.md

If you don't want to push your data from one protocol handler to another but rather actively read it (most likely in an asynchronous fashion) you could also design yourself some interfaces (like ByteReader which implements an async_read(...) method or PacketReader which allows to read complete messages instead of bytes), implement them through your code (and ByteReader also through asio) and use them on the higher level. Thereby you are going from the push approach of data processing to a pull approach, which has some advantages and disadvantages.

Read More

Thursday, April 14, 2016

What kinds of out-of-band failures am I forgetting to test?

Leave a Comment

I came across an amazing presentation years ago (which of course I can't find) that listed a bunch of kinds of failures for remote services that people usually don't test for.

In addition to timeout, 4xx, 5xx, etc, it listed things like:

  • connection closes after 10 bytes of data
  • returns contents of www.google.com
  • returns contents of /dev/random
  • returns contents of /etc/passwd
  • returns correctly-formatted unicode chinese text
  • returns ansi color control characters
  • returns an incorrect content-type, labeled correctly (You requested Content-Type: application/json, I send back Content-Type: application/jpeg)
  • returns one byte of data every 29 seconds

What are some types of "out-of-band failures" you've encountered that developers don't usually (but should) test for?

(extra bonus points if you can find the original presentation)

2 Answers

Answers 1

The ones you listed are great; I'd love to see the original presentation if you dig it up! A couple other favorites:

  • A "valid" response with a couple bits flipped
  • A "valid" response with extra data you weren't expecting ({"result": 123, "extraStuff": {...}}) to simulate upgrades to the remote side
  • A syntactically-valid response that never ends ({"results":["lol", "lol", "lol", ..., or just a bunch of whitespace)

Answers 2

Low-frequency failures. In other words, test that some response is correct not just once, but every time out of a thousand tries. You'll get random Internet breakage if you're going over a network, but you might expose some process is stochastic when you thought it was fixed.

Read More

Sunday, April 10, 2016

end of input at character 0 error on some devices

Leave a Comment

I have following code where I am calling an API which is a PHP built. The code returns json stated as below which I am collecting in a stringBuilder object. Problem is its working on some carriers and on few devices with other carriers / wifi connection its throwing JSONException end of input at character 0 exception, i know this comes when input string is empty, it means stringBuilder object is empty. Problem is i don't have access to the devices on which its throwing these errors.

I am not getting on some device why following code returns empty string and on some its working fine, user has tested on 3G as well as wifi these devices are in other country on different carriers.

            HttpClient httpClient = HttpClientBuilder.create().build();             HttpPost postRequest = new HttpPost(ServiceUrls.base_url + ServiceUrls.get_profile_url);              JSONObject object = new JSONObject();             object.put("username", params[0]);              StringEntity input = new StringEntity(object.toString());             input.setContentType("application/json");             postRequest.setEntity(input);              HttpResponse response = httpClient.execute(postRequest);              if (response.getStatusLine().getStatusCode() != 200) {                 throw new RuntimeException("Failed : HTTP error code : "                         + response.getStatusLine().getStatusCode());             }              BufferedReader br = new BufferedReader(                     new InputStreamReader((response.getEntity().getContent())));              String output;             StringBuilder stringBuilder = new StringBuilder();             while ((output = br.readLine()) != null) {                 stringBuilder.append(output);             } 

If it was for all API call then it was logical but doest happen for other API call, this API call returns bigger size JSON string as follows in stringbuilder

{
"status":1, "parking":{
"name":"ghgjjghghg", "cost":3, "ownerId":29, "address":"xyz pqr", "slots":4, "image":"d4bc95c1dd031685746f2c3570788acf.jpg", "details":"gjhjghjgg", "amenities":"gjhg", "id":70, "lon":73.7898023, "lat":19.9974533, "type":0, "available":1 }, "rating":0, "ratingCount":0, "owner":{
"id":29, "username":"vd@gmail.com", "password":"", "fullname":"vi hdjh", "phone":"23434fddf", "ccNum":null, "ccType":null, "type":1, "authType":1, "image":"582e3a77d76ae3203cfd6d6a346da429.jpg", "dni":"abc123", "account":"ABCBANK" } }

I have no clue whats happening , please help. Any input will be appreciated.

4 Answers

Answers 1

There is nothing unusual about the code you posted. No clues there.

Let me summarize what I think you said about the symptoms.

  • For some devices / carriers, a specific API call fails. But not all devices / carriers.

  • For the same devices / carriers as above, other API calls work, all if the time.

  • The client-side code is identical in all cases, apart from the URLs.

To me, this is pointing at a problem on the server side that is triggered by what the requests look like to it. But either the way, I would try to investigate this by looking at the requests and responses on the server side, and checking the server-side logs. See if there are significant differences in the requests coming from different devices / carriers. Especially the ones that work versus the ones that don't work. And see if the responses are empty when the server sends them.

Answers 2

The code can be improved by checking (before putting it in the string builder) whether the length of the content is bigger than 0.

HttpClient httpClient = HttpClientBuilder.create().build(); HttpPost postRequest = new HttpPost(ServiceUrls.base_url +          ServiceUrls.get_profile_url);  JSONObject object = new JSONObject(); object.put("username", params[0]);  StringEntity input = new StringEntity(object.toString()); input.setContentType("application/json"); postRequest.setEntity(input);  HttpResponse response = httpClient.execute(postRequest);  String output;  if (response.getStatusLine().getStatusCode() != 200) {     throw new RuntimeException("Failed : HTTP error code : "             + response.getStatusLine().getStatusCode()); } else {     HttpEntity entity = response.getEntity();      if ((entity != null) && (entity.getContentLength() != 0)) {         // Use writing to output stream to prevent problems with chunked responses            ByteArrayOutputStream os = new ByteArrayOutputStream();        entity.writeTo(os);        output = new String(os.toByteArray(),"UTF-8");      } else {        // Handle case there is not content returned        System.out.println("Received no content (HTTP response code " + response.getStatusLine().getStatusCode() + " , reason: " + getReasonPhrase() +")");     } } 

The code above however doesn't solve the issue why you get an empty response. I its only handling the fact it is happening is a more elegant way.

I noted however that you require a username in the request. Are you sure the user exist on the device and in case of non existing user, should there be returned something else?

Answers 3

I found the theory of Leonidos usefull: http://stackoverflow.com/a/19540249/6076711

And here is my end of solution you can try using the following code.

string output = ""; while(br.ready() && (br.readLine() != null)) {     output += (String.valueOf(br.readLine()))+"\n"; } 

Answers 4

This highly depends on what you want to do with the StringBuilder object. if you are calling the StringBuilder.toString() function the generated String will be empty, if the StringBuilder had no input. That is meant to be that way.

You could either initialize the StringBuilder with one of these functions:

StringBuilder stringBuilder = new StringBuilder(CharSequence cs); StringBuilder stringBuilder = new StringBuilder(int initCapacity); StringBuilder stringBuilder = new StringBuilder(String s); 

or add a placeholder to the StringBuilder when it is empty:

if(stringBuilder.length() == 0){     stringBuilder.append("NO_INPUT"); } 
Read More

Friday, April 8, 2016

Connect to SQL Server on a different domain via JDBC

Leave a Comment

From Windows using SQL Server Management Studio (SSMS), I can only connect to a SQL Server on a different domain as follows:

C:\> runas /netonly /user:differentDomainName\aUserName "C:\Program Files (x86 )\Microsoft SQL Server\110\Tools\Binn\ManagementStudio\Ssms.exe -S anIpAddress"

How can I accomplish this connection via JDBC? I've tried using the following connection string with Microsoft's sqljdbc 4.2 driver:

jdbc:sqlserver://anIpAddress:1433;database=MAIN;user=differentDomainName\\aUserName;password=pass

I receive the following error:

com.microsoft.sqlserver.jdbc.SQLServerException: Login failed for user 'differentDomainName\aUserName'

This is the same error that I receive if I start SSMS without using runas and typed differentDomainName\aUserName for Login name in the "Connect to Server" dialog box of SSMS 2012.

Additional Information: The JDBC connection will be established within a application running on Linux. So, running the application using runas is not an option unfortunately.

Another attempt: I've also tried to use jTDS 1.3.1 with the following connection string:

jdbc:jtds:sqlserver://anIpAddress:1433;databaseName=MAIN;domain=differentDomainName;user=aUserName;password=pass

since aUserName is set up only for Windows authentication. Unfortunately, this produces the following exception:

o.a.tomcat.jdbc.pool.ConnectionPool : Unable to create initial connections of pool. Followed by java.sql.SQLException: I/O Error: DB server closed connection.

Permission information: I'm unable to modify anything on the SQL Server machine including any configuration within SQL Server. The "aUserName" account maps to a SQL Server read only Windows authentication only user.

1 Answers

Answers 1

When you connect with MS JDBC driver, you don't specify the password for the user (at least not in the connection string you provided). If your intention was to use integrated security, you should indicate this in the connection string, but then you process has to be authenticated already for differentDomainName\aUserName

Integrated security & JDBC: https://msdn.microsoft.com/en-us/library/ms378428%28v=sql.110%29.aspx?f=255&MSPPError=-2147217396#Connectingintegrated

Since your plan is to access SQL server from linux, I doubt that you could make integrated security work for that scenario, so you should plan to provide the password in the connection string. I'm not sure if you can provide username/password for a domain user in the connection string (I think you can), but if you switch to a user with SQL server auth, it will certainly work. This should be a fallback option, as SQL server auth is less secure.

Read More

Wednesday, April 6, 2016

Scapy - How to Dissect an Ethernet Trailer field

Leave a Comment

I'm using the F5 Networks Big-IP products, which are appending a custom Ethernet II trailer frame for debugging purposes. I'm trying with Scapy to bind a new layer for this trailer, but I'm unable to do so.

I can see the payload of interest in the Padding field but using bind_layers does not perform proper dissection of the required Padding section.

class MyEthTrailer(Packet):     name = "Ethernet Trailer"     fields_desc = [ ####Fields Mapping Section ]     def dissect(self, s):         self.payl,self.pad = self.extract_padding(s)         s = self.do_dissect(self.pad) 

One solution I was thinking was to create a new Ethernet replacement class (or overloaded), which I can then refers to the typical Ethernet payload and my new trailer. But I'm not a super Python/scapy programmer, and I am not certain if this is the best option.

This is how Scapy currently maps my packet after I apply bind_layers(TCP,MyEthTrailer). The info I should have parse is in the Padding class

<Ether  dst=00:00:00:00:00:00 src=00:00:00:00:00:01 type=0x8100 |<Dot1Q  prio=0L id=0L vlan=01L type=0x800 |<IP  version=4L ihl=5L tos=0x0 len=67 id=1 flags=DF frag=0L ttl=255 proto=tcp chksum=0x01 src=10.0.0.1 dst=10.0.1.1 options=[] |<TCP  sport=1111 dport=https seq=1 ack=1 dataofs=5L reserved=0L flags=PA window=4380 chksum=0xb718 urgptr=0 options=[] |<MyEthTrailer  |<Padding  load='\xPayload of MyEtherTrailer' |>>>>>> 

[UPDATE-1]

I can force decoding a TCP SYN packet by calling :

packet[TCP].decode_payload_as(MyEthTrailer) 

However, the bind_layers method, does not seem to work automatically, and this does not work with more complex packet because it's mixing up TCP Padding with MyEthTrailer payload.

[UPDATE-2]

I got it partly working, but every packet needs to be casted properly, then I can read the trailer payload and decode it. For example if a packet is TCP/DNS/MyEthTrailer, this will work. If I don't know it's DNS, and it's not set up properly, it's still mixed in in TCP payload and Padding.

Your help is appreciated.

0 Answers

Read More

Sunday, March 27, 2016

OSX: Check if the default interface is physical or virtual

Leave a Comment

Is there way on OSX to see if the default interface is an actual physical interface or if it is a virtual interface like when it is connected to a VPN?

Essentially, I am trying to figure out an interface property that can tell if the default interface is virtual or physical. We can read interface properties from scutil system configuration utility framework.

0 Answers

Read More