I am trying to get the server IPs from a steam master server via a query and later query the game servers.
https://developer.valvesoftware.com/wiki/Master_Server_Query_Protocol#
There are plenty of examples in different programming languages but not dart.
https://gist.github.com/koenbollen/645947/09241258c0d2dd8d5a4e647865730ab83955f68b#file-mastersteam-py-L18
My problem is: i don't even understand why the address "hl2master.steampowered.com" is an invalid internet address. It says so on the official valve Master Server Query (link at top of the post).
This is the error i get trying to run the code:
[ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: Invalid argument(s): Invalid internet address hl2master.steampowered.com
import 'dart:io';
import 'dart:convert';
// Master server:
const GOLD_SRC = "hl1master.steampowered.com";
const GOLD_SRC_PORT = 27010;
const SOURCE = "hl2master.steampowered.com";
const SOURCE_PORT = 27011;
// Regions:
const US_EAST_COAST = 0x00;
const US_WEST_COAST = 0x01;
const SOUTH_AMERICA = 0x02;
const EUROPE = 0x03;
const ASIA = 0x04;
const AUSTRALIA = 0x05;
const MIDDLE_EAST = 0x06;
const AFRICA = 0x07;
const ALL = 0xFF;
class MasterServerQuery {
connectSocket01() async {
var message = "31 FF 30 2E 30 2E 30 2E";
InternetAddress master = InternetAddress(SOURCE);
var masterPort = SOURCE_PORT;
RawDatagramSocket.bind(InternetAddress.anyIPv4, 4096).then((
RawDatagramSocket socket) {
print('UDP Echo ready to receive');
print('${socket.address.address}:${socket.port}');
socket.listen((RawSocketEvent e) {
Datagram d = socket.receive();
if (d == null) return;
String message = "31 FF 30 2E 30 2E 30 2E";
print(
'Datagram from ${d.address.address}:${d.port}: ${message.trim()}');
socket.send(message.codeUnits, master, masterPort);
});
});
}
}
The address is not valid, since it expects a valid ip address and not an hostname, you can provide the ip address or use InternetAddress.lookup.
You code is also wrong in the way that you pass the message you shouldn't pass the codeUnits but the bytes in a way as such:
final byteData = ByteData(8);
byteData.setUint8(0, 0x31);
byteData.setUint8(1, 0xFF);
byteData.setUint8(2, 0x30);
byteData.setUint8(3, 0x2E);
byteData.setUint8(4, 0x30);
byteData.setUint8(5, 0x2E);
byteData.setUint8(6, 0x30);
byteData.setUint8(7, 0x2E);
byteData.buffer.asUint8List() //Pass this.
Related
#include <ESP8266WiFi.h>;
#include <WiFiClient.h>;
#include <ThingSpeak.h>;
const char* ssid = "Wifi_Project"; //Your Network SSID
const char* password = "1111aaaa"; //Your Network Password
WiFiClient client;
unsigned long myChannelNumber = 1947152; //Your Channel Number (Without Brackets)
const char * myWriteAPIKey = "QA02MFMGFJVIDBZR"; //Your Write API Key
#include <SoftwareSerial.h>
SoftwareSerial SMESerial (D6, D7);
int xVal, yVal, zVal;
float xtotal,ytotal,ztotal,xprice,yprice,zprice;
void setup() {
Serial.begin(9600);
SMESerial.begin(9600);
WiFi.begin(ssid, password);
ThingSpeak.begin(client);
}
void loop() {
if (SMESerial.available()<1) return;
char R=SMESerial.read();
int data=SMESerial.parseInt();
if (R == 'x')
xVal = data;
xtotal = xVal + xtotal;
xprice = xtotal*0.1;
Serial.print("X = ");
Serial.println(xtotal);
ThingSpeak.setField(1, xtotal);
ThingSpeak.setField(2, xprice);
if (R == 'y')
yVal = data;
ytotal = yVal + ytotal;
yprice = ytotal*0.1;
Serial.print("Y = ");
Serial.println(ytotal);
ThingSpeak.setField(3, ytotal);
ThingSpeak.setField(4, yprice);
if (R == 'z')
zVal = data;
ztotal = zVal + ztotal;
zprice = ztotal*0.1;
Serial.print("Z = ");
Serial.println(ztotal);
ThingSpeak.setField(5, ztotal);
ThingSpeak.setField(6, zprice);
ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
}
I've made a wireless sensor networks project by using 3 slave nodemcu esp8266 to collect current reading using ACS712 and send it to master nodemcu esp8266. The master nodemcu should send data to phone to view the data through a mobile application. I used Thingspeak at first to view data of the power consumption but i want to use google firebase to view the power consumption easier for my application view. Need help please.
IMPORTANT EDIT
After further investigating, I found out that the packet size is in fact much larger than the stated 1024 bytes, the 1024 bytes were just the limit of the standard out I was using (android studio / flutter).
Some of the packets received are now up to ~27 000 bytes large, however that is nowhere near the actually transmitted size of over 10x that.
I am trying to send singular packets of up to 5 MB in length over a Socket connection in Dart. For this, I am using the following code:
Socket socket = await Socket.connect(globals.serverUrl, globals.serverPort);
Stream<Uint8List> stream = socket?.asBroadcastStream();
Uint8List? response = await stream?.first;
String responseString = String.fromCharCodes(response);
Note that my Server is running Java while the Client is using Dart.
After sending the data packet from the Server to the Client, it successfully receives the first 1024 bytes exactly of the packet, and the rest is nowhere to be found, even after reading stream.first multiple times, they continuously read the newly sent packet and not the remaining bytes of the old packet.
So my question is, how do I require the Socket stream to read ALL bytes of the packet until finished, and not just the first 1024?
EDIT:
The received packet on the client is parsed using:
String? decrypt(String cipherText, String keyString) {
final key = Key.fromUtf8(keyString);
final iv = IV.fromBase64(cipherText.split(":")[1]);
final encrypter = Encrypter(AES(key, mode: AESMode.cbc, padding: null));
final encrypted = Encrypted.fromBase64(cipherText.split(":")[0]);
final decrypted = encrypter.decrypt(encrypted, iv: iv);
globals.log.i("DECRYPTED: $decrypted");
return decrypted;
}
The error that I am getting stems from getting the IV, since the message is cut off at 1024 bytes and the ":" appears much later in the String.
The problem is that the Dart socket split messages bigger than 1024 bytes into multiple packets of 1024 bytes. So there's some approaches you can use to combine them together in the client:
By extending Socket class
I do not believe this is a right solution:
Hard to extend since it's a platform implementation (you can see the sdk implementation of dart:io almost any class method is external).
Hard to maintain.
Since it depends on custom platform implementations you need to do it on multiple platforms.
It's easy to create undocumented memory leaks.
Let me know if you still prefer this approach I'll do a further research.
By using Stream<T>.reduce function
The problem with this approach in your context is that Sockets do not emit a done event when a message is sent by using socket.write('Your message').
So unless you're using a socket to send a single message this function can't help you cause it will return a Future<T> that will never complete (only when the socket connection is closed).
By emitting a EOF message from the server
This is a solution I found even not so elegant, improvements are welcome.
The idea is to concatenate all client received packets into a single one and stop receiving when a pre-determined termination (EOF) string is received.
Implementation
Below is a server implementation that emits a message of 5mb followed by a message:end string every time a new client is connected.
import 'dart:io';
Future<void> main() async {
final ServerSocket serverSocket =
await ServerSocket.bind(InternetAddress.anyIPv4, 5050);
final Stream<Socket> serverStream = serverSocket.asBroadcastStream();
serverStream.listen((client) async {
print(
'New client connected: ${client.address}:${client.port} ${client.done} Remote address: ${client.remoteAddress}');
const int k1byte = 8;
const int k2bytes = k1byte * 2;
const int k1kb = k1byte * 1000;
const int k1mb = k1kb * 1000;
const int k5mb = k1mb * 5;
// Create a 5mb string that follows: '1000.....0001'
final String k1mbMessage = '1${createStringOf(k5mb - k2bytes, '0')}1';
client.write(k1mbMessage);
client.write('message:end');
});
print('Listening on: ${serverSocket.address} ${serverSocket.port}');
}
String createStringOf(int size, [String char = ' ']) {
// https://api.dart.dev/stable/2.17.3/dart-core/String-class.html it says:
// > A sequence of UTF-16 code units.
// And from https://www.ibm.com/docs/en/db2-for-zos/12?topic=unicode-utfs says:
// > UTF-16 is based on 16-bit code units. Each character is encoded as at least 2 bytes.
int dartStringEncodingSize = 2;
assert(size >= dartStringEncodingSize && size.isEven,
'''Dart char contains 2 bytes so we can only create Strings (with exact size) of even N bytes''');
assert(char.length == 1, '''[char] must be a single char String''');
int charCount = size ~/ dartStringEncodingSize;
return char * charCount;
}
And here we can see a client implementation where we use 'our own reduce' function that combine all packets while the termination string is not found.
import 'dart:io';
Future<void> main() async {
final Socket server = await Socket.connect('localhost', 5050);
final Stream<String> serverSocket =
server.asBroadcastStream().map(String.fromCharCodes); // Map to String by default
const kMessageEof = 'message:end';
String message = '';
await for (String packet in serverSocket) {
// If you are using [message] as a List of bytes (Uint8List):
// message = [...Uint8List.fromList(message), ...Uint8List(packet)]
message += packet;
// Do not compare directly packet == kMessageEof
// cause it can be 'broken' into multiple packets:
// -> 00000 (packet 1)
// -> 00000 (packet 2)
// -> 00mes (packet 3)
// -> sage: (packet 4)
// -> end (packet 5)
if (message.endsWith(kMessageEof)) {
// remove termination string
message = message.replaceRange(
message.length - kMessageEof.length,
message.length,
'',
);
}
print('Received: $message'); // Prints '1000000......0000001'
}
}
You can make it more generic if you want by using an extension:
import 'dart:io';
/// This was created since the native [reduce] says:
/// > When this stream is done, the returned future is completed with the value at that time.
///
/// The problem is that socket connections does not emits the [done] event after
/// each message but after the socket disconnection.
///
/// So here is a implementation that combines [reduce] and [takeWhile].
extension ReduceWhile<T> on Stream<T> {
Future<T> reduceWhile({
required T Function(T previous, T element) combine,
required bool Function(T) combineWhile,
T? initialValue,
}) async {
T initial = initialValue ?? await first;
await for (T element in this) {
initial = combine(initial, element);
if (!combineWhile(initial)) break;
}
return initial;
}
}
Future<void> main() async {
final Socket server = await Socket.connect('localhost', 5050);
final Stream<String> serverSocket =
server.asBroadcastStream().map(String.fromCharCodes);
const kMessageEof = 'message:end';
// Reduce with a condition [combineWhile]
String message = await serverSocket.reduceWhile(
combine: (previous, element) => '$previous$element',
combineWhile: (message) => !message.endsWith(kMessageEof),
);
// Remove termination string
message = message.replaceRange(
message.length - kMessageEof.length,
message.length,
'',
);
print('Received: $message');
}
Since the socket itself doesn't send the done event the way I found to reduce all packets into a single one was by emitting 'our own done event'.
I am interfacing with the Microsoft Health Cloud API and have successfully requested an access token and refresh token. Communication with the RESTful API works as intended, although I am having a hard time figuring out, how to reliably determine an expired access token.
I have the following code in place:
fire_and_forget read_profile()
{
HttpClient httpClient{};
httpClient.DefaultRequestHeaders().Authorization({ L"bearer", access_token_ });
try
{
auto const response{ co_await httpClient.GetStringAsync({ L"https://api.microsofthealth.net/v1/me/Profile" }) };
// Raise event passing the response along.
// Code left out for brevity.
co_return;
}
catch (hresult_error const& e)
{
if (e.code() != 0x80190191) // Magic value for "unauthorized access (401)"
{
throw;
}
// This is an "unauthorized access (401)" error. Continue with requesting a new
// access token from the refresh token.
// Code left out for brevity.
}
Although it appears to work, it feels wrong for so many reasons. It's not just the magic value, but also the fact, that this particular error code may be used for other error modes.
Is there a more robust way of determining, whether an access token has expired?
Note: I understand, that I could use the expiration interval, and check against the system time. I'd rather not go down that route, as it isn't entirely reliable either, and introduces additional complexity for roaming that information across devices.
I understand, that I could use the expiration interval, and check against the system time.
Microsoft Health Cloud API has provided expires_in field to verify the token is valid. In general, we could check against the system time, and if the system time was artificially modified, it isn't entirely reliable. So we could use NTP server time, rather than use system time.
public async static Task<DateTime> GetNetworkTime()
{
//default Windows time server
const string ntpServer = "time.windows.com";
// NTP message size - 16 bytes of the digest (RFC 2030)
var ntpData = new byte[48];
//Setting the Leap Indicator, Version Number and Mode values
ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
var addresses = await Dns.GetHostAddressesAsync(ntpServer);
//The UDP port number assigned to NTP is 123
var ipEndPoint = new IPEndPoint(addresses[0], 123);
//NTP uses UDP
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
socket.Connect(ipEndPoint);
//Stops code hang if NTP is blocked
socket.ReceiveTimeout = 3000;
socket.Send(ntpData);
socket.Receive(ntpData);
socket.Dispose();
}
//Offset to get to the "Transmit Timestamp" field (time at which the reply
//departed the server for the client, in 64-bit timestamp format."
const byte serverReplyTime = 40;
//Get the seconds part
ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
//Get the seconds fraction
ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
//Convert From big-endian to little-endian
intPart = SwapEndianness(intPart);
fractPart = SwapEndianness(fractPart);
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
//**UTC** time
var networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((long)milliseconds);
return networkDateTime.ToLocalTime();
}
// stackoverflow.com/a/3294698/162671
static uint SwapEndianness(ulong x)
{
return (uint)(((x & 0x000000ff) << 24) +
((x & 0x0000ff00) << 8) +
((x & 0x00ff0000) >> 8) +
((x & 0xff000000) >> 24));
}
I have been looking around and have been trying to figure out how to query the dhcp server I am connected to and get all options available or to at least be able to query with a proper option in mind and get that information in response. Ideally getting all option information from the lease would be great but I really only need it for a specific option that I want to use to package/receive information with.
I have been looking at this in hopes to figure out how to alter it but I am not all that familiar with the D language, making it a cumbersome task. http://blog.thecybershadow.net/2013/01/10/dhcp-test-client/
CODE:
module dhcptest;
import core.thread;
import std.algorithm;
import std.array;
import std.conv;
import std.random;
import std.stdio;
import std.string;
import std.socket;
version(Windows)
import std.c.windows.winsock : ntohs, htons, ntohl, htonl;
else
version(Posix)
import core.sys.posix.netdb : ntohs, htons, ntohl, htonl;
else
static assert(false, "Unsupported platform");
/// Header (part up to the option fields) of a DHCP packet, as on wire.
align(1)
struct DHCPHeader
{
align(1):
/// Message op code / message type. 1 = BOOTREQUEST, 2 = BOOTREPLY
ubyte op;
/// Hardware address type, see ARP section in "Assigned Numbers" RFC; e.g., '1' = 10mb ethernet.
ubyte htype;
/// Hardware address length (e.g. '6' for 10mb ethernet).
ubyte hlen;
/// Client sets to zero, optionally used by relay agents when booting via a relay agent.
ubyte hops;
/// Transaction ID, a random number chosen by the client, used by the client and server to associate messages and responses between a client and a server.
uint xid;
/// Filled in by client, seconds elapsed since client began address acquisition or renewal process.
ushort secs;
/// Flags. (Only the BROADCAST flag is defined.)
ushort flags;
/// Client IP address; only filled in if client is in BOUND, RENEW or REBINDING state and can respond to ARP requests.
uint ciaddr;
/// 'your' (client) IP address.
uint yiaddr;
/// IP address of next server to use in bootstrap; returned in DHCPOFFER, DHCPACK by server.
uint siaddr;
/// Relay agent IP address, used in booting via a relay agent.
uint giaddr;
/// Client hardware address.
ubyte[16] chaddr;
/// Optional server host name, null terminated string.
char[64] sname = 0;
/// Boot file name, null terminated string; "generic" name or null in DHCPDISCOVER, fully qualified directory-path name in DHCPOFFER.
char[128] file = 0;
/// Optional parameters field. See the options documents for a list of defined options.
ubyte[0] options;
static assert(DHCPHeader.sizeof == 236);
}
/*
35 01 02
0F 17 68 6F 6D 65 2E 74 68 65 63 79 62 65 72 73 68 61 64 6F 77 2E 6E 65 74
01 04 FF FF FF 00
06 04 C0 A8 00 01
03 04 C0 A8 00 01
05 04 C0 A8 00 01
36 04 C0 A8 00 01
33 04 00 00 8C A0
FF
*/
struct DHCPOption
{
ubyte type;
ubyte[] data;
}
struct DHCPPacket
{
DHCPHeader header;
DHCPOption[] options;
}
enum DHCPOptionType : ubyte
{
subnetMask = 1,
timeOffset = 2,
router = 3,
timeServer = 4,
nameServer = 5,
domainNameServer = 6,
domainName = 15,
leaseTime = 51,
netbiosNodeType = 46,
dhcpMessageType = 53,
serverIdentifier = 54,
renewalTime = 58,
rebindingTime = 59,
}
enum DHCPMessageType : ubyte
{
discover = 1,
offer ,
request ,
decline ,
ack ,
nak ,
release,
inform
}
enum NETBIOSNodeType : ubyte
{
bNode = 1,
pNode,
mMode,
hNode
}
DHCPPacket parsePacket(ubyte[] data)
{
DHCPPacket result;
enforce(data.length > DHCPHeader.sizeof + 4, "DHCP packet too small");
result.header = *cast(DHCPHeader*)data.ptr;
data = data[DHCPHeader.sizeof..$];
enforce(data[0..4] == [99, 130, 83, 99], "Absent DHCP option magic cookie");
data = data[4..$];
ubyte readByte()
{
enforce(data.length, "Unexpected end of packet");
ubyte result = data[0];
data = data[1..$];
return result;
}
while (true)
{
auto optionType = readByte();
if (optionType==0) // pad option
continue;
if (optionType==255) // end option
break;
auto len = readByte();
DHCPOption option;
option.type = optionType;
foreach (n; 0..len)
option.data ~= readByte();
result.options ~= option;
}
return result;
}
ubyte[] serializePacket(DHCPPacket packet)
{
ubyte[] data;
data ~= cast(ubyte[])((&packet.header)[0..1]);
data ~= [99, 130, 83, 99];
foreach (option; packet.options)
{
data ~= option.type;
data ~= to!ubyte(option.data.length);
data ~= option.data;
}
data ~= 255;
return data;
}
string ip(uint addr) { return format("%(%d.%)", cast(ubyte[])((&addr)[0..1])); }
void printPacket(DHCPPacket packet)
{
auto opNames = [1:"BOOTREQUEST",2:"BOOTREPLY"];
writefln(" op=%s\n chaddr=%(%02X:%)\n hops=%d\n xid=%08X\n secs=%d\n flags=%04X\n ciaddr=%s\n yiaddr=%s\n siaddr=%s\n giaddr=%s\n sname=%s\n file=%s",
opNames.get(packet.header.op, text(packet.header.op)),
packet.header.chaddr[0..packet.header.hlen],
packet.header.hops,
packet.header.xid,
ntohs(packet.header.secs),
ntohs(packet.header.flags),
ip(packet.header.ciaddr),
ip(packet.header.yiaddr),
ip(packet.header.siaddr),
ip(packet.header.giaddr),
to!string(packet.header.sname.ptr),
to!string(packet.header.file.ptr),
);
writefln(" %d options:", packet.options.length);
foreach (option; packet.options)
{
auto type = cast(DHCPOptionType)option.type;
writef(" %s: ", type);
switch (type)
{
case DHCPOptionType.dhcpMessageType:
enforce(option.data.length==1, "Bad dhcpMessageType data length");
writeln(cast(DHCPMessageType)option.data[0]);
break;
case DHCPOptionType.netbiosNodeType:
enforce(option.data.length==1, "Bad netbiosNodeType data length");
writeln(cast(NETBIOSNodeType)option.data[0]);
break;
case DHCPOptionType.subnetMask:
case DHCPOptionType.router:
case DHCPOptionType.timeServer:
case DHCPOptionType.nameServer:
case DHCPOptionType.domainNameServer:
case DHCPOptionType.serverIdentifier:
enforce(option.data.length % 4 == 0, "Bad IP option data length");
writefln("%(%s, %)", map!ip(cast(uint[])option.data).array());
break;
case DHCPOptionType.domainName:
writeln(cast(string)option.data);
break;
case DHCPOptionType.timeOffset:
case DHCPOptionType.leaseTime:
case DHCPOptionType.renewalTime:
case DHCPOptionType.rebindingTime:
enforce(option.data.length % 4 == 0, "Bad integer option data length");
writefln("%(%d, %)", map!ntohl(cast(uint[])option.data).array());
break;
default:
writefln("%(%02X %)", option.data);
}
}
}
enum SERVER_PORT = 67;
enum CLIENT_PORT = 68;
__gshared UdpSocket socket;
void listenThread()
{
try
{
static ubyte[0x10000] buf;
ptrdiff_t received;
Address address;
while ((received = socket.receiveFrom(buf[], address)) > 0)
{
auto receivedData = buf[0..received].dup;
try
{
auto packet = parsePacket(receivedData);
writefln("Received packet from %s:", address);
printPacket(packet);
}
catch (Exception e)
writefln("Error while parsing packet [%(%02X %)]: %s", receivedData, e.toString());
}
throw new Exception(format("socket.receiveFrom returned %d.", received));
}
catch (Exception e)
{
writeln("Error on listening thread:");
writeln(e.toString());
}
}
void sendPacket()
{
DHCPPacket packet;
packet.header.op = 1; // BOOTREQUEST
packet.header.htype = 1;
packet.header.hlen = 6;
packet.header.hops = 0;
packet.header.xid = uniform!uint();
packet.header.flags = htons(0x8000); // Set BROADCAST flag - required to be able to receive a reply to an imaginary hardware address
foreach (ref b; packet.header.chaddr[0..packet.header.hlen])
b = uniform!ubyte();
packet.options ~= DHCPOption(DHCPOptionType.dhcpMessageType, [DHCPMessageType.discover]);
writefln("Sending packet:");
printPacket(packet);
socket.sendTo(serializePacket(packet), new InternetAddress("255.255.255.255", SERVER_PORT));
}
void main()
{
socket = new UdpSocket();
socket.setOption(SocketOptionLevel.SOCKET, SocketOption.BROADCAST, 1);
try
{
socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, 1);
socket.bind(getAddress("0.0.0.0", CLIENT_PORT)[0]);
writefln("Listening for DHCP replies on port %d.", CLIENT_PORT);
}
catch (Exception e)
{
writeln("Error while attempting to bind socket:");
writeln(e);
writeln("Replies will not be visible. Use a packet capture tool to see replies,\nor try re-running the program with more permissions.");
}
(new Thread(&listenThread)).start();
writeln("Type \"d\" to broadcast a DHCP discover packet.");
while (true)
{
auto line = readln().strip().split();
if (!line.length)
{
writeln("Enter a command.");
continue;
}
switch (line[0].toLower())
{
case "d":
case "discover":
sendPacket();
break;
default:
writeln("Unrecognized command.");
}
}
}
From my understanding I will have to send a BOOTP vendor extension if I want to query for a specific option. I am not a networking guru and am looking for as much help as possible thank you.
DHCP OPTIONS DOCUMENTATION:
http://www.networksorcery.com/enp/protocol/bootp/options.htm
If you only need a one-off experiment, you can modify the sendPacket function to include the DHCP option you need. Notice the line:
packet.options ~= DHCPOption(DHCPOptionType.dhcpMessageType, [DHCPMessageType.discover]);
You can copy and edit it to add more options to the DHCP packet.
As for decoding, the program will already print all the data it receives, but it may not know how to decode every option. You may need to add more options to the respective enumerations, and to the switch statement that controls how these options are parsed and printed.
I am trying to use Libnet11 function:
int libnet_write_raw_ipv6 (libnet_t *l, u_int8_t *packet, u_int32_t size)
to inject IPv6 packet on network layer.
I had created IPv6 packet and captured it
with Wireshark. Wireshark reported:
malformed packet(wireshark says that next
header value in IPv6 is wrong and payload
size is too big in my opinion)
I hope, someone could help me with minimal code example,
showing how to manually build IPv6 packet (with ICMPv6
extension header) with libnet11 (libnet_write_raw_ipv6()).
I assume that the minimal code might look like this:
packet_len = 40 + 16; // 40B ~ IPv6 packet, 16B ~ ICMPv6 header
u_char *buf = NULL;
struct ip6_hdr *ip6 = NULL;
struct icmp6_hdr *icmp6 = NULL;
l = libnet_init();
if ( (buf = malloc(packet_len)) == NULL ) {
// error
}
// create IPv6 header
ip6 = (struct ip6_hdr *) buf;
ip6->ip6_flow = 0;
ip6->ip6_vfc = 6 << 4;
ip6->ip6_plen = 16; // ICMPv6 packet size
ip6->ip6_nxt = IPPROTO_ICMPV6; // 0x3a
ip6->ip6_hlim = 64;
memcpy(&(ip6->ip6_src), &src_addr, sizeof(struct in6_addr));
memcpy(&(ip6->ip6_dst), &dst_addr, sizeof(struct in6_addr));
// create ICMPv6 header
icmp6 = (struct icmp6_hdr *) (buf + 40); // 40B ~ IPv6 packet size
icmp6->icmp6_type = ICMP6_ECHO_REQUEST;
icmp6->icmp6_code = 0;
icmp6->icmp6_cksum= 0;
icmp6->icmp6_data32[0] = 0;
libnet_do_checksum(l, (u_int8_t *)buf, IPPROTO_ICMPV6, packet_len);
written = libnet_write_raw_ipv6(l, buf, packet_len);
if ( written != packet_len )
perror("Failed to send packet");
libnet_destroy(l);
free(buf);
I tried to find code examples but with no success.
Thank you in advance.
Martin
If you're using C++, then I'd recommend you libtins, a packet crafting a sniffing library. This short snippet does exactly what you want:
#include <tins/tins.h>
using namespace Tins;
void test(const IPv6Address &dst, const IPv6Address &src) {
PacketSender sender;
IPv6 ipv6 = IPv6(dst, src) / ICMPv6();
ipv6.hop_limit(64);
sender.send(ipv6);
}
int main() {
// now use it
test("f0ef:1234::1", "f000::1");
}
You can create it with raw sockets though. I also had to do something similar but couldn't find anything as a reference.
To do it with raw sockets, this link gives you a nice explanation