LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   send a packet through sockets c++ (https://www.linuxquestions.org/questions/programming-9/send-a-packet-through-sockets-c-859997/)

vbx_wx 02-01-2011 08:04 AM

send a packet through sockets c++
 
Can someone help me with this problem,trying to send a packet,using tcp/ip,the packet should look like this:
Code:

field 1: SOCKS protocol version, 1 byte (0x05 for this version)
field 2: status, 1 byte:
        0x00 = request granted
        0x01 = general failure
        0x02 = connection not allowed by ruleset
        0x03 = network unreachable
        0x04 = host unreachable
        0x05 = connection refused by destination host
        0x06 = TTL expired
        0x07 = command not supported / protocol error
        0x08 = address type not supported
field 3: reserved, must be 0x00
field 4: address type, 1 byte:
        0x01 = IPv4 address
        0x03 = Domain name
        0x04 = IPv6 address
field 5: destination address of
        4 bytes for IPv4 address
        1 byte of name length followed by the name for Domain name
        16 bytes for IPv6 address
field 6: network byte order port number, 2 bytes

and this is my code:
Code:

int domainLen = strlen(domain);
char reply[domainLen + 7];
reply[0] = 5; // version
reply[1] = 0; // succed
reply[2] = 0; // reserved
reply[3] = 3; // its a domain
reply[4] = domainLen;; // lenght of domain
for(int j = 0; j < domainLen; ++j)
{
        reply[j + 5] = domain[j];
}
reply[5 + domainLen] = 80; // port
reply[20] = '\0';
Send(reply, sizeof(reply));

domain is "www.google.com". Am I doing it right ? I dont know much about bits.

Snark1994 02-01-2011 10:29 AM

I think you're close... I've never done TCP/IP before, but from my understanding of the last part of the protocol description :
Quote:

field 5: destination address of
4 bytes for IPv4 address
1 byte of name length followed by the name for Domain name
16 bytes for IPv6 address
field 6: network byte order port number, 2 bytes
then your penultimate line doesn't make much sense. You're setting the 21st byte to be zero... I don't think you need to null-terminate the packet (and this line isn't doing that anyway). So, I think you just need to remove the
Code:

reply[20] = '\0';
. Also, I'm pretty sure that in network programming they use Big-Endian byte order, so
Code:

reply[5+domainLen] = 80;
may not be doing what you expect - if I'm right, then this will be 2^8*80, or 20480, not 80. But as I said, this is just from reading RFC 1928 and a little knowledge, so perhaps a more experienced network programmer will know better :)

vbx_wx 02-01-2011 10:45 AM

Thanks for reply,maybe thats the problem with the Big-Endian byte order,because am pretty sure that the packet is good(i made some changes to it).Hope anyone else have an opion too.

dwhitney67 02-01-2011 12:36 PM

Quote:

Originally Posted by vbx_wx (Post 4244790)
Thanks for reply,maybe thats the problem with the Big-Endian byte order,because am pretty sure that the packet is good(i made some changes to it).Hope anyone else have an opion too.

If you are indeed programming in C++, you could consider defining an object that represents the Socks5 packet that you require. Consider the following (untested) code. Btw, I haven't a clue about Sock5 (documentation is scant on the low-level API, and Wiki is of no real help), so I may have made some bad assumptions below; use at your own risk.

Code:

truct Sock5_Packet
{
  enum Status { REQUEST, GEN_FAILURE, CONN_NOT_ALLOWED, NETWORK_UNREACHABLE, HOST_UNREACHABLE, CONN_REFUSED, TTL_EXPIRED, CMD_NOT_SUPPORTED, ADDR_NOT_SUPPORTED };

  enum AddrType { IPV4 = 1, DOMAIN = 3, IPV6 = 4 };

  Sock5() : version(5), reserved(0) {}

  unsigned char  version;
  unsigned char  status;
  unsigned char  reserved;
  unsigned char  addrType;

  union DestAddr
  {
      unsigned char ipv4[4];

      struct Domain
      {
        unsigned char len;
        unsigned char name[255];
      } domain;

      unsigned char ipv6[16];
  };

  DestAddr dest;

  unsigned short port;
};


#include <string>
#include <iostream>
#include <cassert>
#include <arpa/inet.h>

extern int Send(const void* data, const size_t size);

int main()
{
  const std::string    domain("www.google.com");
  const unsigned short port = 80;

  assert(domain.size() <= 255);

  Sock5_Packet packet;

  packet.status          = Sock5::REQUEST;
  packet.addrType        = Sock5::DOMAIN;
  packet.dest.domain.len = domain.size();
  packet.port            = htons(port);

  memcpy((void*) packet.dest.domain.name, domain.c_str(), domain.size());

  Send(&packet, sizeof(packet));

  // ...
}


orgcandman 02-04-2011 04:16 PM

Quote:

Originally Posted by dwhitney67 (Post 4244880)
If you are indeed programming in C++, you could consider defining an object that represents the Socks5 packet that you require. Consider the following (untested) code. Btw, I haven't a clue about Sock5 (documentation is scant on the low-level API, and Wiki is of no real help), so I may have made some bad assumptions below; use at your own risk.
...

Your example is not the best. You have no idea how the compiler will structure "packet." And by that, I mean, you don't know if it's aligned properly (likely, it won't be with those unsigned chars) and you also have a "random" 255 byte structure in there.

An even better approach would be to modify your Sock5_packet structure to be a class with a serialize() / deserialize() interface, which takes a uint8_t * and a uint32_t reference. I'm envisioning the following:

Code:

#include <string>
#include <iostream>
#include <cassert>
#include <arpa/inet.h>
#include <string.h>

extern int Send(const void* data, const size_t size);

class Sock5_Packet
{
public:
    enum Status { REQUEST=0, GEN_FAILURE, CONN_NOT_ALLOWED, NETWORK_UNREACHABLE, HOST_UNREACHABLE, CONN_REFUSED, TTL_EXPIRED, CMD_NOT_SUPPORTED, ADDR_NOT_SUPPORTED, UNKNOWN_STATUS };
  enum AddrType { UNKNOWN_TYPE=0, IPV4 = 1, DOMAIN = 3, IPV6 = 4 };

  Sock5_Packet();
  virtual ~Sock5_Packet();

  inline void    setStatus(Status status) { m_Status = status; }
  inline void    setPort(uint16_t port)  { m_Port  = htons(port); }

  int32_t setDestAddr(AddrType type, const uint8_t *bytes, uint32_t length);

  inline const uint16_t getPort() const  { return (const uint16_t)ntohs(m_Port); }
  inline const Status  getStatus() const { return (const Status)m_Status; }

  void getDestAddr(AddrType &type, uint8_t *bytes, uint32_t &length) const;

  int32_t serialize(uint8_t *, uint32_t &);
  int32_t deserialize(uint8_t *, uint32_t &);

private:

  Status  m_Status;
  AddrType m_AddrType;
  uint8_t *m_DestAddrBytes;
  uint32_t m_DestAddrBytesLen;
  uint16_t m_Port;


}; 

Sock5_Packet :: Sock5_Packet() : m_Status(UNKNOWN_STATUS), m_AddrType(UNKNOWN_TYPE), m_DestAddrBytes(0), m_DestAddrBytesLen(0), m_Port(0)
{
}

Sock5_Packet :: ~Sock5_Packet()
{
    if(m_DestAddrBytes) delete [] m_DestAddrBytes;
}

int32_t
Sock5_Packet :: setDestAddr(AddrType type, const uint8_t *bytes,
                            uint32_t length)
{
    uint8_t *oldBytes = m_DestAddrBytes;

    if(!bytes || !length) return -1;

    switch(type)
    {
    case IPV4:
        if(length != 4) return -1;
        m_DestAddrBytes = new uint8_t[4];
        *((uint32_t*)m_DestAddrBytes) = htonl(*((uint32_t*)bytes));
        break;

    case DOMAIN:
        if(length > 255) return -1;
        m_DestAddrBytes = new uint8_t[length];
        strncpy((char *)m_DestAddrBytes, (char *)bytes, length);
        m_DestAddrBytes[length - 1] = 0;
        break;

    case IPV6:
        if(length != 16) return -1;
        m_DestAddrBytes = new uint8_t[16];
        memcpy(m_DestAddrBytes, bytes, 16);
        break;

    default:
        return -1;
    }
    m_AddrType = type;
    m_DestAddrBytesLen = length;
    delete [] oldBytes;
    return 0;
}

void
Sock5_Packet :: getDestAddr(AddrType &type, uint8_t *bytes, uint32_t &length) const
{
    type = UNKNOWN_TYPE;
    if(m_AddrType == type)          return;
    if(length < m_DestAddrBytesLen) return;
    if(!bytes)                      return;
    memcpy(bytes, m_DestAddrBytes, m_DestAddrBytesLen);
    length = m_DestAddrBytesLen;
    type  = m_AddrType;
}

int32_t
Sock5_Packet :: serialize(uint8_t *out_bytes, uint32_t &out_byte_len)
{
    //256 is "magic" and should really be changed
    uint8_t buf[256] = {0};
    if(!out_bytes) return -1;

    // total size needed to serialize the packet ...
    uint32_t nTotalSizeNeeded = 6; // 6 is "magic" since it's less than the minimum size
    switch(m_AddrType)
    {
    case IPV4:
        nTotalSizeNeeded += 4;
        memcpy(buf, m_DestAddrBytes, m_DestAddrBytesLen);
        break;

    case DOMAIN:
        nTotalSizeNeeded += 1 + m_DestAddrBytesLen;
        buf[0] = (uint8_t)m_DestAddrBytesLen;
        memcpy(buf+1, m_DestAddrBytes, m_DestAddrBytesLen);
        break;

    case IPV6:
        nTotalSizeNeeded += 16;
        memcpy(buf, m_DestAddrBytes, m_DestAddrBytesLen);
        break;

    default:
        return -1;
    }

    if(out_byte_len < nTotalSizeNeeded) return -1;

    out_bytes[0] = 5;
    out_bytes[1] = (uint8_t)m_Status;
    out_bytes[2] = 0x00;
    out_bytes[3] = (uint8_t)m_AddrType;
    memcpy(out_bytes+3, buf, nTotalSizeNeeded - 6);
    *((uint16_t*)(out_bytes + nTotalSizeNeeded - 4)) = m_Port;

    out_byte_len = nTotalSizeNeeded;

    return 0;
}

int32_t
Sock5_Packet :: deserialize(uint8_t *, uint32_t &)
{
    return -1; // exercise for the reader ....
}


int main()
{
  const std::string    domain("www.google.com");
  const unsigned short port = 80;
  uint8_t outbuf[1024] = {0};
  uint32_t outbuflen = 1024;
  assert(domain.size() <= 255);

  Sock5_Packet packet;

  packet.setStatus(Sock5_Packet::REQUEST);
  packet.setPort(80);
  packet.setDestAddr(Sock5_Packet::DOMAIN, (const uint8_t *)domain.c_str(), domain.size());

  if(packet.serialize(outbuf, outbuflen) < 0) cout << "Failed to serialize\n";

  Send(outbuf, outbuflen);
  // ...
}


vbx_wx 02-04-2011 04:53 PM

@orgcandman: very nice code.

dwhitney67 02-04-2011 06:40 PM

Quote:

Originally Posted by orgcandman (Post 4248639)
Your example is not the best. You have no idea how the compiler will structure "packet."

...


All times are GMT -5. The time now is 12:15 AM.