0

I've been set a task to write some software to communicate from our embedded linux device to a server. The server that I'm to communicate with uses a strict protocol, the protocol is very obscure and proprietary - I wouldn't like to name it on here as the information could be sensitive to the company I work for.

So the data must be in either the form of 4 bit Nibbles (N), 32 bit unsigned ints (U), 32 bit signed ints (S), 8 bit unsigned ints (X) and chars (C). So for example a simplified login structure might be NNNN-User ID followed by XX-some more data, CCCC-access code. So I need to send NNNNXXCCCC in that order to login.

The data needs to be sent via UDP, and then listen for an acknowledgement on the same port. So the way I've done this is I've written a send_and_receive function, and then coded a struct, to send the struct through to the server.

     typedef u_int8_t NN;
     typedef u_int8_t X;
     typedef int32_t S;
     typedef u_int32_t U;
     typedef char C;

     #pragma pack(1)

     typedef struct{

     NN user_id[2];
     X some_data[2];
     C access_code[4];
     } LogOnRequest;

I then declare and fill in the information for the struct and send it using this function:

void send_and_receive(void* message, void* reply, int do_send, int expect_reply){
struct sockaddr_in serv_addr;
int sockfd, i, slen=sizeof(serv_addr);
int buflen = BUFLEN;
void* buf = NULL;

if ( (strlen(message)) >= BUFLEN)
    err("Message too big");

buf = malloc(buflen);

if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
    err("socket");

bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if (inet_aton(IP_ADDRESS, &serv_addr.sin_addr)==0)
    err("inet_aton() failed\n");

if(do_send == TRUE){
    strcpy(buf, message);
    if (sendto(sockfd, buf, buflen, 0, (struct sockaddr*)&serv_addr, slen)==-1)
        err("sendto()");
}

if (expect_reply == TRUE){
    if (recvfrom(sockfd, buf, buflen, 0, (struct sockaddr*)&serv_addr, &slen)==-1)
        err("recvfrom()");
}

memcpy(reply, buf, BUFLEN);
close(sockfd);
free(buf);
}

Now when I do this I get no reply, and no notice that the packet has been received on the server. Basically I would like to know if I'm doing this correctly, is there a better way to do it? Would it be possible to do the same thing using bash scripts?

Any feedback would be great, because I'm feeling out of my depth on this one.

3
  • 2
    if (sendto(sockfd, buf, buflen, will write the complete buffer, even beyond the terminating NUL character. Commented Sep 18, 2013 at 20:57
  • OK, do you suggest that I limit the size of the buffer to the size of the incoming structure? If so I will look into that. Commented Sep 18, 2013 at 21:20
  • No, I suggest you use the actual length of the payload (overhead+ strlen()) as the third argument to the send() function. Padding the packet with zero bytes is useles. Commented Sep 18, 2013 at 21:29

2 Answers 2

2

You need to wait some amount of time for a reply. If you don't get one, you need to retransmit the query and wait again. UDP is a "best effort" delivery service and does not have any built in retries.

You also need to make sure you are sending multi-byte values the same way the other end expects them. Which byte comes first?

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for your comment, I'll add in a timeout loop for the send/receive section. I know that the other server is big-endian if that's what you're referring too? I've never written anything like this before, I'm assuming that when I give the pointer to the struct that addresses the first bit in the struct, and the pack(1) means there will be no padding between struct fields, does this sound correct?
@Kells1986 Probably. But I wouldn't do it that way. Personally, I'd assemble the data byte by byte myself so that I don't have to rely on that kind of thing.
OK, I have literally no experience with this kind of thing, are you saying that you would send the required bytes in separate packets? Or do I need to do something like work out how much memory I need for a whole command and write the required values to that?
@Kells1986 I mean I wouldn't use a struct and expect the way my machine lays out information to match how the protocol expects it. I would assemble the data, byte by byte, in memory and then send it.
Ok, thanks, I will definitely look into assembling the data myself vs structs, Wireshark should tell me if the data is in the right format.
2

As usual in such cases, I'd like to encourage you to use WireShark or any other sniffer software. This will de-cypher all the magic happens after your sendto() attempt: bytes order, actual address/port applied, any feedback received etc. And yes, as @wildplasser said in the comments, you shouldn't send all the buffer. So you have to add "message size" as an input parameter.

2 Comments

Thanks, I attempted using WireShark yesterday, the matter was complicated by the fact that the client code was running on a VM so the packet is first send over the NAT before reaching being sent to the external server, which, thinking about it, could also be another issue.
Is it possible to install WireShark exactly on the VM? Not sure if WS can sniff a transit traffic from VM to outer world.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.