Sending raw TCP packets


(Euan) #1

Hi all,

Totally new to ST and Groovy. I’m trying to send raw TCP to a device, but totally unsure where to start. I’ve done it in Python, but can anyone offer any assistance in porting this to Groovy?

Any help gratefully received.

In python it’s relatively easy:

import socket

IPADDR = '192.168.1.111’
PORTNUM = 8888

data = bytes.fromhex(‘520500050106EF’)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
s.connect((IPADDR, PORTNUM))
s.send(data)
s.close()


(joe) #2

I have not found a way to do that inside a smart app. The closest I’ve come is with the sendhubcommand to access devices on my local lan.

private hubApiGet(apiPath) {	

	def userpassascii = "${state.j64User}:${state.j64Password}"
	def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
    
    def headers = [:] 
    headers.put("HOST", j64AlarmServerAddress())
    headers.put("Authorization", userpass)

	def result = new physicalgraph.device.HubAction(
 		   	method: "GET",
    		path: apiPath,
    		headers: headers
		)
    sendHubCommand(result)
}

You can also use httpGet() calls if you need to call out to an external service.


#5

Tagging @pstuart @jared @jody.albritton


(Adam Kempenich) #6

From my understanding of reading @pstuart’s and @jared’s documentation on this, you can send raw TCP packets UP TO 0x7F, but anything else greater than that doesn’t transmit properly.

Here is an example that I have worked up (somebody please correct me if this is wrong):

byte[] bytes = [0xCC, 0x23, 0x33] // These are the bytes to send to our device
String str = new String(bytes)    // Convert the bytes into an ASCII string
log.debug "${str}"                // Debug the output
new physicalgraph.device.HubAction("${str}", physicalgraph.device.Protocol.LAN, "0A0001A6:374D")
// The above line's parameters are as follows: The data to send, the protocol for transmitting the data, and the IP ...
// ... to send the data to (in hex). In this case, the IP and port I'm sending data locally to is 10.0.1.166:5577

This results in an output of >> “Ì#3”, but that’s not correct: in ascii, it should be represented as “\cc#3”.

Does anyone know how to properly send unsigned bytes?


(Patrick Stuart [@pstuart]) #7

Can’t. Submit a ticket. It’s a bug in the hubaction. We need more people to claim this as an issue. Needs to get priority.


(Adam Kempenich) #8

Submitted one yesterday about this issue. Got a response stating to ask other forum members about it. Too bad, because TCP—even without a response—would be incredibly helpful…


(Jared) #9

It’s a joke. Everything about HubAction sucks. That’s all I can add to the conversation that hasn’t been said already.

I’ve already forgotten the terminology, but what I ended up hacking together was sending bytecode that would intentionally be converted (by ST) to the UTF-8 (hex) signed equivalent, and then stringing together those bytes to form the command I needed.

So for instance, using your example, I would find a byte sequence that maybe ended in 0xCC, then another that started with 0x23 and 0x33. It was super hacky and, as you can guess, still only supported certain combinations of bytes, but I mean I did get it “working”

last edit: after a quick look here: http://www.utf8-chartable.de/ , I couldn’t find a UTF8 combination that would work.


(Adam Kempenich) #10

Thanks, Jared. Unfortunately, you’re 100% correct, there seem to be more combinations that don’t work, than do.
For documentation’s sake, using extended ASCII characters doesn’t work, either. So, in a method similar to yours, sending an extended ASCII code of 0xCC, or 204 (╠) doesn’t work, either.


#11

Don’t convert to ASCII. ASCII is 7bit so you’ll have some loss of data

Convert to either UTF-8 or ISO-8859-1.

byte[] bData = getMyByteData();
String sData = new String(body, "UTF-8");
sendHubCommand(new physicalgraph.device.HubAction(sData, physicalgraph.device.Protocol.LAN, getDataValue("mac"), [callback: calledBackHandler]));

Now if somebody can tell me how to get BACK a raw packet, that’d be very much appreciated.


(Ph4r) #12

How are you getting from bData to body?
I think you use the deviceID to hold the hex host and port that you connect to, is this correct?

I played with things for quite some time and I still can’t get the raw data that has a 1 in the significant bit to transfer directly with either of these encodings.


(Ph4r) #13

I gave up getting this to work, so for now I am using a cygwin man-in-the-middle solution. Should you like to use it as well for a personal project, you can find it on my github.

source only - no binaries available at this time.

I then use the following for how to send example data from a DTH:

byte[] bytes = [0x71, 0x24, 0x0F, 0xA3]
String body = bytes.encodeHex()
sendHubCommand(new physicalgraph.device.HubAction(body.toString(), physicalgraph.device.Protocol.LAN, getDataValue("mac")))

Then once you have done a configure and make you start the application such as this:

./tcptunnel --local-port=5577 --remote-port=5577 --remote-host=<Local IP Address of target device> --log --stay-alive

BTW - I am encoding and decoding, so it should work for returned data as well, but I have so far only tested the ability to send data out from SmartThings.


#14

I’m sorry I didn’t get back to you.

The code I shared with you was older code. I was trying to build a TLS transport layer with SmartThings. I was able to output data just fine, just I can’t receive byte which made my work pointless.

I’m using ISO-8859-1 which shouldn’t have the collision issues when converting to and from String and Byte[] types. It’s my suspicion that SmartThings, internally, takes the String object and calls String.getBytes() with no encoding specified. It would then use the object’s specified encoding which should keep the data 1:1.

def hosthex = getDataValue("ip");
def porthex = getDataValue("port")
def target = "$hosthex:$porthex";
device.deviceNetworkId = target;

byte[] body = buildTLSClientHello();

log.debug "${body.length} ${bytesToHex(body)}";
String strBody = new String(body, "ISO-8859-1");

return sendHubCommand(new physicalgraph.device.HubAction(strBody, physicalgraph.device.Protocol.LAN, getDataValue("mac")));

Here are a couple of functions I built that may serve useful for you, or others trying to send byte data:

// Build random block of 32bit integers
byte[] buildRandomData(size) {
  ByteArrayOutputStream out = new ByteArrayOutputStream();    
  writeInt(out, (int)Math.floor(new Date().getTime() / 1000), 32);
  for(def i = 0; i < size - 4; i++)
    out.write((int)(Math.random() * 0xFF));
  out.flush();
  return out.toByteArray();
}

// Write int of varying bit-size (8bit, 16bit, 24bit, 32bit, etc)
public static void writeInt(ByteArrayOutputStream out, int value, int bits) {
  for(def i = bits - 8; i >= 0; i-=8) {
    out.write((byte) (0xFF & (value >> i)));
  }
}

// Return hex-string interpretation of byte array
public static String bytesToHex(byte[] bytes) {
  final char[] hexArray = "0123456789ABCDEF".toCharArray();
  char[] hexChars = new char[bytes.length * 2];
  for ( int j = 0; j < bytes.length; j++ ) {
    int v = bytes[j] & 0xFF;
    hexChars[j * 2] = hexArray[v >>> 4];
    hexChars[j * 2 + 1] = hexArray[v & 0x0F];
  }
  return new String(hexChars);
}

Raw TCP Socket Communications with sendHubCommand()
(Ph4r) #15

Thanks for trying; however, that does not work on my hub at least. :confused:

I changed one line from above to be"

byte[] body = [0x71, 0x24, 0x0F, 0xA4]

and when it sends, the value that I receive is:

71 24 0F C2 A4

It injected an extra [C2] byte to handle the A4 which is > 7F. I’ve tried this with other value as well previously and it would return very different values the higher up you go such that the last byte didn’t match the value requested.


Magic Home WiFi LED Control
(Mike Maxwell) #16

Yea, this has been the problem all along, it won’t do 8 bit asci…


(Ph4r) #17

BUMP, any chance there has been any prioritization in the product plan to include this support?


(Guillaume Lecomte) #18

Not really a direct help for the discussed issue but mozilla has an interesting product that might interest people frustrated by SmartThings limitations:

https://hacks.mozilla.org/2018/02/how-to-build-your-own-private-smart-home-with-a-raspberry-pi-and-mozillas-things-gateway/

It’s probably not for everyone but for me it hits just the right spot between HomeAssistant and SmartThings (take care of remote access including basic mobile compatible UI, but run locally and remain fully open and easily extendable, already contains rules engine, …). Running node is also a bonus to me (writing extensions with good IDE support will be nice).