bsc geth smartcontract doesn't react to eth_call - docker

In my project - I try to iterate with smartcontracts(bep20/erc20). I've implemented some dummy bep20/erc20 smartcontracts and prepared docker images to run with premined deposited addresses my integrational tests. In eth chain I'have no problems - I can get balance and send my tokens between addresses. But in bsc chain - I have a problem - no reaction. I can't nither work with balances or transfers nor with contract info(totalSupply/decimals/symbol/name functions don't respond anything.)
bsc(geth) source: https://github.com/binance-chain/bsc (master)
entrypoint docker-image script body
/usr/local/bin/geth --config ${DATA_DIR}/config.toml --datadir ${DATA_DIR} \
--password /passphrase \
init /genesis.json
echo "IMPORTING DEPOSIT_ADDRESS: $DEPOSIT_ADDRESS <= $DEPOSIT_ADDRESS_PRIVATE_KEY"
/usr/local/bin/geth --config ${DATA_DIR}/config.toml --datadir ${DATA_DIR} \
--password /passphrase \
account import <(echo $DEPOSIT_ADDRESS_PRIVATE_KEY)
/usr/local/bin/geth --config ${DATA_DIR}/config.toml --datadir ${DATA_DIR} \
--verbosity 5 --log.debug --nousb \
--password /passphrase \
--fakepow --nodiscover --maxpeers 0 \
--http.api personal,db,eth,net,web3 \
--networkid 1999 --mine --miner.threads=2 --miner.etherbase $DEPOSIT_ADDRESS --vmdebug
ibep20.sol file:
// SPDX-License-Identifier: MIT
// pragma solidity ^0.5.1;
pragma solidity ^0.8.13;
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.0.0/contracts/token/ERC20/IERC20.sol
interface IBEP20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
bep20.sol
// SPDX-License-Identifier: MIT
// pragma solidity ^0.5.0;
pragma solidity ^0.8.13;
import "./ibep20.sol";
contract BEP20 is IBEP20 {
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
string public name = "usdt";
string public symbol = "usdt";
uint8 public decimals = 6;
function transfer(address recipient, uint256 amount) external returns (bool) {
balanceOf[msg.sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(msg.sender, recipient, amount);
return true;
}
function approve(address spender, uint amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transferFrom(
address sender,
address recipient,
uint amount
) external returns (bool) {
allowance[sender][msg.sender] -= amount;
balanceOf[sender] -= amount;
balanceOf[recipient] += amount;
emit Transfer(sender, recipient, amount);
return true;
}
function mint(uint amount) external {
balanceOf[msg.sender] += amount;
totalSupply += amount;
emit Transfer(address(0), msg.sender, amount);
}
function burn(uint amount) external {
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
// constructor() {
constructor() public {
uint256 correctBalance = 1000 * 10 ** decimals;
// uint correctBalance = 1000 * 10 ** decimals;
balanceOf[msg.sender] = correctBalance;
totalSupply = correctBalance;
emit Transfer(address(0), msg.sender, correctBalance);
}
}
I've tried to compile it with solc(v0.5.*) and with (0.8.13) - It didn't help.
In geth logs I see that smartcontract was deployed successfully
INFO [09-19|15:13:29.500|internal/ethapi/api.go:2080] Submitted contract creation hash=0x3f42c1dba1f53977db93677663b6d56ac8cb51681295ff93d7f047f308045ccc from=0xd3aE0500e21008c89CA4746be7522340C67F5730 nonce=0 contract=0x4B1Ed695C76af5Dbd305AFA41FBC3E7002e735D4 value=0
and Also eth_getTransactionByHash shows that it was mined(0x3f42c1dba1f53977db93677663b6d56ac8cb51681295ff93d7f047f308045ccc)
and when I try to call smart contract:
➜ bsc git:(dev) ✗ curl -X POST http://127.0.0.1:10000 -H "Content-Type: application/json" --data \
> '{"jsonrpc":"2.0","method":"eth_call","params":[{"to": "0x4b1ed695c76af5dbd305afa41fbc3e7002e735d4", "data": "0x70a08231000000000000000000000000d3ae0500e21008c89ca4746be7522340c67f5730"}, "latest"],"id":1}'
{"jsonrpc":"2.0","id":1,"result":"0x"}
and in geth logs:
DEBUG[09-19|15:21:24.534|internal/ethapi/api.go:906] Executing EVM call finished runtime="158.331µs"
DEBUG[09-19|15:21:24.534|rpc/handler.go:313] Served eth_call conn=127.0.0.1:43962 reqid=1 duration="308.631µs"

Related

how to call an external function in YUL?

How can I accomplish the next behavior(more specific to implement callMint) in Yul? I couldn't find anything related to this
contract Token is ERC20{
....
function mint(address addr, uint256 amount) external {
_mint(addr, amount);
}
....
}
struct Param{
address owner;
uint256 amount;
}
contract Test{
....
function callMint(address tokenAddress, Param param) external {
Token(tokenAddress).mint(param.owner, param.amount);
}
....
}
Basically you need to copy the calldata for the external function you intend to do into memory. In your case the calldata is the abi.encode of the selector (4 bytes) and the two arguments.
I haven't tested it, but it should look like this:
function callMint(address tokenAddress, Param param) external {
address _owner = param.owner;
uint256 _amount = param.amount;
bytes4 _selector = 0x40c10f19; // bytes4(keccak('mint(address,uint256)'))
assembly {
// We need 0x44 bytes for the calldata in memory.
// We use the free memory pointer to find where to save it.
let calldataOffset := mload(0x40)
// We move the free memory pointer, but if this function is
// 'external' we don't really need to
mstore(0x40, add(calldataOffset, 0x44))
// Store salldata to memory
mstore(calldataOffset, _selector)
mstore(add(calldataOffset, 0x04), _owner)
mstore(add(calldataOffset, 0x24), _amount)
let result := call(
gas(),
tokenAddress,
0, // msg.value
calldataOffset,
0x44,
0, // return data offset
0 // return data length
)
// Revert if call failed
if eq(result, 0) {
// Forward the error
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
}
}
}

How to extend Dart sockets BroadcastStream buffer size of 1024 bytes?

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'.

Call Delphi dll from go

I have a Delphi dll that needs to be called from golang (go). I can load and call the dll using syscall.Syscall, or windows.Call. Both methods execute the dll correctly. The dll returns its result by changing a string buffer that is passed to it. When I later inspect the string, it is empty (in syscall) or panics in windows.Call. I have tried several permutations but haven't had any luck getting my value out of the variable that should store the result.
Example using windows.Call:
// Package getSID calls SIDgenerator and generates a SID for Retail Pro
package main
import (
"fmt"
"unsafe"
"golang.org/x/sys/windows"
"unicode/utf16"
)
var (
sidgeneratorDLL, _ = windows.LoadDLL("sidgenerator64.dll")
getRandomSID, _ = sidgeneratorDLL.FindProc("GetRandomSID")
)
// StringToCharPtr converts a Go string into pointer to a null-terminated cstring.
// This assumes the go string is already ANSI encoded.
func StringToCharPtr(str string) *uint8 {
chars := append([]byte(str), 0) // null terminated
return &chars[0]
}
// GetRandomSID generates random SID, UPC SID or ALU SID
func GetRandomSID(Buffer []uint16, BufSize uint64) (result byte) {
// var nargs uintptr = 2
fmt.Println(Buffer)
ret, _, callErr := getRandomSID.Call(uintptr(unsafe.Pointer(&Buffer)),
uintptr(unsafe.Pointer(&BufSize)))
// fmt.Println("Buffer", Buffer)
if callErr != nil {
fmt.Println("===== CallErr =====")
fmt.Println(ret, callErr)
// fmt.Println("Buffer", Buffer)
}
result = byte(ret)
return
}
func main() {
defer sidgeneratorDLL.Release()
buffer := utf16.Encode([]rune("12345678901234567890\000"))
bufSize := uint64(len(buffer))
fmt.Printf("Result in: %v\n", buffer)
err := GetRandomSID(buffer, bufSize)
fmt.Printf("Result in: %v\n", buffer)
fmt.Println("Err =", err)
fmt.Println("Called GetRandomSID")
fmt.Printf("Result out: %v\n", buffer)
}
func init() {
fmt.Print("Starting Up\n")
}
Example using syscall:
// Package getSID calls SIDgenerator and generates a SID for Retail Pro
package main
import (
"fmt"
"syscall"
"unsafe"
)
var (
sidgeneratorDLL, _ = syscall.LoadLibrary("sidgenerator64.dll")
getRandomSID, _ = syscall.GetProcAddress(sidgeneratorDLL, "GetRandomSID")
)
// GetRandomSID generates random SID, UPC SID or ALU SID
func GetRandomSID(Buffer *[]byte, BufSize *uint32) (result byte) {
var nargs uintptr = 2
fmt.Println(*Buffer)
ret, _, callErr := syscall.Syscall(uintptr(getRandomSID),
nargs, 0,
uintptr(unsafe.Pointer(Buffer)),
uintptr(unsafe.Pointer(BufSize)),
)
fmt.Println(*Buffer)
if callErr != 0 {
fmt.Println("===== CallErr =====")
fmt.Println(callErr)
}
result = byte(ret)
return
}
func main () {
defer syscall.FreeLibrary(sidgeneratorDLL)
buffer := []byte("1234567890123456789012")
bufSize := uint32(len(buffer))
fmt.Printf("Result in: %s\n", string(buffer))
err := GetRandomSID(&buffer, &bufSize)
fmt.Println("Err =", err)
fmt.Println("Called GetRandomSID")
fmt.Printf("Result out: %s\n", string(buffer))
}
func init() {
fmt.Print("Starting Up\n")
}
Any help will be greatly appreciated. Thanks in advance!
C# external declarations: --------------------------------
[DllImport("SIDGenerator.dll", CharSet = CharSet.Ansi)] static extern Boolean GetSIDFromALU(string ALU, StringBuilder SID, ref int buflen);
[DllImport("SIDGenerator.dll", CharSet = CharSet.Ansi)] static extern Boolean GetSIDFromUPC( string UPC, StringBuilder SID, ref int buflen);
[DllImport("SIDGenerator.dll", CharSet = CharSet.Ansi)] static extern Boolean GetRandomSID(StringBuilder SID, ref int buflen);
Delphi external declarations: -----------------------------------
function GetSIDFromALU( ALU: Pchar; Buffer: PChar; var BufSize : integer ): LongBool; stdcall; external ‘SIDGenerator.dll’;
function GetSIDFromUPC( UPC: PChar; Buffer: PChar; var BufSize : integer ): LongBool; stdcall; external ‘SIDGenerator.dll’;
function GetRandomSID( Buffer: PChar; var BufSize : integer ): LongBool; stdcall; external ‘SIDGenerator.dll’;
In Python I call it with this code and it works beautifully:
# Get Retail Pro SID using dll
from ctypes import windll, create_string_buffer, c_int, byref
# os.chdir("c:\\Users\\irving\\Documents\\gitHub\\POImport")
# print(os.getcwd())
# getSID is a Retail Pro dll use to generate SIDs
getSID = windll.SIDGenerator64
randomSID = getSID.GetRandomSID
aluSID = getSID.GetSIDFromALU
upcSID = getSID.GetSIDFromUPC
# Set this by hand now, later set it interactively or by parameter
subsidiary = "1"
def getRandomSID():
""" Genera un SID aleatorio """
# Generate a new random SID
newSID = create_string_buffer(str.encode("12345678901234567890"))
size = c_int(len(newSID))
randomSID(newSID, byref(size))
return newSID.value
def getALUSID(alu):
""" Genera un SID basado en ALU """
# Generate a new ALU base SID
ALU = create_string_buffer(str.encode(alu))
newSID = create_string_buffer(str.encode("12345678901234567890"))
size = c_int(len(newSID))
aluSID(ALU, newSID, byref(size))
return newSID.value
def getUPCSID(upc):
""" Genera un SID basado en UPC """
# Generate a new UPC based SID
UPC = create_string_buffer(str.encode(upc))
newSID = create_string_buffer(str.encode("12345678901234567890"))
size = c_int(len(newSID))
upcSID(UPC, newSID, byref(size))
return newSID.value
You are passing the pointer to the slice, instead of a pointer to the backing array of the slice.
With your windows.Call code it should be.
ret, _, callErr := getRandomSID.Call(uintptr(unsafe.Pointer(&Buffer[0])),
uintptr(unsafe.Pointer(&BufSize)))
Or your syscall.Syscall version, where its a pointer to the slice.
ret, _, callErr := syscall.Syscall(uintptr(getRandomSID),
nargs, 0,
uintptr(unsafe.Pointer(&(*Buffer)[0])),
uintptr(unsafe.Pointer(BufSize)),
)

NodeMCU JSON POST data issue

I am using the below code for sending POST json requests.
I am receiving the request, but the body parameter is coming as null. ("body": null)
// Example of the different modes of the X.509 validation options
// in the WiFiClientBearSSL object
//
// Mar 2018 by Earle F. Philhower, III
// Released to the public domain
#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>
#include <time.h>
#include <ArduinoJson.h>
#ifndef STASSID
#define STASSID "biranchi#xxx"
#define STAPSK "0xxxxx4"
#endif
const char *ssid = STASSID;
const char *pass = STAPSK;
const uint16_t port = 443;
const char * host = "xxxxxx.execute-api.ap-southeast-1.amazonaws.com";
const char * path = "/dev/";
// Set time via NTP, as required for x.509 validation
void setClock() {
configTime(3 * 3600, 0, "pool.ntp.org", "time.nist.gov");
Serial.print("Waiting for NTP time sync: ");
time_t now = time(nullptr);
while (now < 8 * 3600 * 2) {
delay(500);
Serial.print(".");
now = time(nullptr);
}
Serial.println("");
struct tm timeinfo;
gmtime_r(&now, &timeinfo);
Serial.print("Current time: ");
Serial.print(asctime(&timeinfo));
}
// Try and connect using a WiFiClientBearSSL to specified host:port and dump HTTP response
void fetchURL(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *path) {
if (!path) {
path = "/";
}
StaticJsonDocument<300> doc;
doc["key11"] = "Value11";
doc["time"] = 1232323;
// serializeJson(doc, Serial);
char buffer[100];
serializeJson(doc, buffer);
Serial.println(buffer);
// String result = "{\"Action\":\"hi\",\"DeviceID\":\"1234567890\"}";
// int len1 = len(result);
size_t len = strlen(buffer);
ESP.resetFreeContStack();
uint32_t freeStackStart = ESP.getFreeContStack();
Serial.printf("Trying: %s:443...", host);
client->connect(host, port);
if (!client->connected()) {
Serial.printf("*** Can't connect. ***\n-------\n");
return;
}
Serial.printf("Connected!\n-------\n");
//client->write("GET ");
client->write("POST ");
client->write(path);
client->write(" HTTP/1.0\r\nHost: ");
client->write(host);
// client->write("\r\nUser-Agent: ESP8266\r\n");
client->write("\r\nAccept: */*");
client->write("\r\nContent-Type: application/x-www-form-urlencoded");
// client->write("\r\nContent-Length: " + len);
client->write("\r\n");
client->write("\r\n");
client->write(buffer);
uint32_t to = millis() + 5000;
if (client->connected()) {
do {
char tmp[32];
memset(tmp, 0, 32);
int rlen = client->read((uint8_t*)tmp, sizeof(tmp) - 1);
yield();
if (rlen < 0) {
break;
}
// Only print out first line up to \r, then abort connection
char *nl = strchr(tmp, '\r');
if (nl) {
*nl = 0;
Serial.print(tmp);
break;
}
Serial.print(tmp);
} while (millis() < to);
}
client->stop();
uint32_t freeStackEnd = ESP.getFreeContStack();
Serial.printf("\nCONT stack used: %d\n-------\n\n", freeStackStart - freeStackEnd);
}
void fetchNoConfig() {
Serial.printf(R"EOF(
If there are no CAs or insecure options specified, BearSSL will not connect.
Expect the following call to fail as none have been configured.
)EOF");
BearSSL::WiFiClientSecure client;
fetchURL(&client, host, port, path);
}
void fetchInsecure() {
Serial.printf(R"EOF(
This is absolutely *insecure*, but you can tell BearSSL not to check the
certificate of the server. In this mode it will accept ANY certificate,
which is subject to man-in-the-middle (MITM) attacks.
)EOF");
BearSSL::WiFiClientSecure client;
client.setInsecure();
fetchURL(&client, host, port, path);
}
void fetchFingerprint() {
Serial.printf(R"EOF(
The SHA-1 fingerprint of an X.509 certificate can be used to validate it
instead of the while certificate. This is not nearly as secure as real
X.509 validation, but is better than nothing.
)EOF");
BearSSL::WiFiClientSecure client;
static const char fp[] PROGMEM = "5F:F1:60:31:09:04:3E:F2:90:D2:B0:8A:50:38:04:E8:37:9F:BC:76";
client.setFingerprint(fp);
fetchURL(&client, host, port, path);
}
void fetchSelfSigned() {
Serial.printf(R"EOF(
It is also possible to accept *any* self-signed certificate. This is
absolutely insecure as anyone can make a self-signed certificate.
)EOF");
BearSSL::WiFiClientSecure client;
Serial.printf("First, try and connect to a badssl.com self-signed website (will fail):\n");
fetchURL(&client, "self-signed.badssl.com", 443, "/");
Serial.printf("Now we'll enable self-signed certs (will pass)\n");
client.allowSelfSignedCerts();
fetchURL(&client, "self-signed.badssl.com", 443, "/");
}
void fetchKnownKey() {
Serial.printf(R"EOF(
The server certificate can be completely ignored and its public key
hardcoded in your application. This should be secure as the public key
needs to be paired with the private key of the site, which is obviously
private and not shared. A MITM without the private key would not be
able to establish communications.
)EOF");
// Extracted by: openssl x509 -pubkey -noout -in servercert.pem
static const char pubkey[] PROGMEM = R"KEY(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAy+3Up8qBkIn/7S9AfWlH
Od8SdXmnWx+JCIHvnWzjFcLeLvQb2rMqqCDL5XDlvkyC5SZ8ZyLITemej5aJYuBv
zcKPzyZ0QfYZiskU9nzL2qBQj8alzJJ/Cc32AWuuWrPrzVxBmOEW9gRCGFCD3m0z
53y6GjcmBS2wcX7RagqbD7g2frEGko4G7kmW96H6dyh2j9Rou8TwAK6CnbiXPAM/
5Q6dyfdYlHOCgP75F7hhdKB5gpprm9A/OnQsmZjUPzy4u0EKCxE8MfhBerZrZdod
88ZdDG3CvTgm050bc+lGlbsT+s09lp0dgxSZIeI8+syV2Owt4YF/PdjeeymtzQdI
wQIDAQAB
-----END PUBLIC KEY-----
)KEY";
BearSSL::WiFiClientSecure client;
BearSSL::PublicKey key(pubkey);
client.setKnownKey(&key);
fetchURL(&client, host, port, path);
}
void fetchCertAuthority() {
static const char digicert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
+OkuE6N36B9K
-----END CERTIFICATE-----
)EOF";
Serial.printf(R"EOF(
A specific certification authority can be passed in and used to validate
a chain of certificates from a given server. These will be validated
using BearSSL's rules, which do NOT include certificate revocation lists.
A specific server's certificate, or your own self-signed root certificate
can also be used. ESP8266 time needs to be valid for checks to pass as
BearSSL does verify the notValidBefore/After fields.
)EOF");
BearSSL::WiFiClientSecure client;
BearSSL::X509List cert(digicert);
client.setTrustAnchors(&cert);
Serial.printf("Try validating without setting the time (should fail)\n");
fetchURL(&client, host, port, path);
Serial.printf("Try again after setting NTP time (should pass)\n");
setClock();
fetchURL(&client, host, port, path);
}
void fetchFaster() {
Serial.printf(R"EOF(
The ciphers used to set up the SSL connection can be configured to
only support faster but less secure ciphers. If you care about security
you won't want to do this. If you need to maximize battery life, these
may make sense
)EOF");
BearSSL::WiFiClientSecure client;
client.setInsecure();
uint32_t now = millis();
fetchURL(&client, host, port, path);
uint32_t delta = millis() - now;
client.setInsecure();
client.setCiphersLessSecure();
now = millis();
fetchURL(&client, host, port, path);
uint32_t delta2 = millis() - now;
std::vector<uint16_t> myCustomList = { BR_TLS_RSA_WITH_AES_256_CBC_SHA256, BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA };
client.setInsecure();
client.setCiphers(myCustomList);
now = millis();
fetchURL(&client, host, port, path);
uint32_t delta3 = millis() - now;
Serial.printf("Using more secure: %dms\nUsing less secure ciphers: %dms\nUsing custom cipher list: %dms\n", delta, delta2, delta3);
}
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
fetchNoConfig();
fetchInsecure();
fetchFingerprint();
fetchSelfSigned();
fetchKnownKey();
fetchCertAuthority();
fetchFaster();
}
void loop() {
// Nothing to do here
}
Am I missing something ?
you've set your content-type header to x-www-form-urlencoded and then you're sending JSON data. you should change that to application/json.

Print the "showname" field attribute in tshark

Context
I've got a pcap file containing a bunch of beacon frames (in other words, I put my Wi-Fi adapter in monitor mode, started capturing while filtering on "wlan.fc.type_subtype == 0x08", and saved that).
Now, I want to, somehow, display specific fields of these packets. Among others:
SSID (wlan_mgt.ssid)
MAC (wlan.ta)
Current channel (wlan_mgt.ds.current_channel)
Group Cipher (wlan_mgt.rsn.gcs.type)
PairWise Ciphers (wlan_mgt.rsn.pcs.type)
Authentication Suite (wlan_mgt.rsn.akms.type)
I don't really care about the representation: plain text, xml, json, csv, X. I'm fine with it. I just don't want more data than I really need and the output needs to be meaningful to the human (wireshark newb) eye.
Eventually, I also want to filter the pcap to get a unique set and count the occurrences (some "|sort|uniq -c" will do), but let's not go there for now.
My solution so far
The first step could be, for example:
$ tshark -r capture.pcap -c 1 -T fields -e wlan_mgt.ssid -e wlan.ta -e wlan_mgt.ds.current_channel -e wlan_mgt.rsn.gcs.type -e wlan_mgt.rsn.pcs.type -e wlan_mgt.rsn.akms.type
MySSID XX:XX:XX:XX:XX:XX 2 4 4 2
After (manually) matching the numbers to their textual meaning, you get this:
SSID = MySSID
MAC (wlan.ta) = XX:XX:XX:XX:XX:XX
Current channel = 2
Group Cipher = Group Cipher Suite type: AES (CCM) (4)
PairWise Ciphers = Pairwise Cipher Suite type: AES (CCM) (4)
Authentication Suite = Auth Key Management (AKM) type: PSK (2)
This is what I'm looking for. But, as stated, I have to do it manually, which is not an option.
Question
Above you can see my current approach to the said goal. By doing
tshark -r capture.pcap -c 1 -T pdml
I get, for example (cutout):
<field name="wlan_mgt.rsn.pcs.list" showname="Pairwise Cipher Suite List 00-0f-ac (Ieee8021) AES (CCM)" size="4" pos="112" show="" value="">
<field name="wlan_mgt.rsn.pcs" showname="Pairwise Cipher Suite: 00-0f-ac (Ieee8021) AES (CCM)" size="4" pos="112" show="1027076" value="000fac04">
<field name="wlan_mgt.rsn.pcs.oui" showname="Pairwise Cipher Suite OUI: 00-0f-ac (Ieee8021)" size="3" pos="112" show="4012" value="000fac"/>
<field name="wlan_mgt.rsn.pcs.type" showname="Pairwise Cipher Suite type: AES (CCM) (4)" size="1" pos="115" show="4" value="04"/>
</field>
</field>
..., which tells me that tshark does have the information I need (in the form of the "showname" attribute).
Apparently, when working with "-T fields -e X", tshark outputs the value that's in the "show" attribute". I feel like I want what's behind the "showname" attribute. Unfortunately, after annoying google for a while I still don't know how or if this is even possible.
I'm also open to radically different ideas, but the main takeaway is that I can't part from the pcap file (which rules out iwlist, kismet, etc). I also preferably don't start writing search and replace rules to replace the meaningless numbers with their textual representation. I hope to solve it in cleaner way.
I kept messing with tshark for a while, until I decided that it couldn't be done. A little bit of programming using the amazing C++ library libtins got me where I needed to be.
The source is down below. Enjoy :)
#include <tins/tins.h>
#include <algorithm>
#include <iostream>
#include <map>
#include <string>
using namespace Tins;
using namespace std;
/*
* Container class for the data that is retrieved from the beacon.
*/
class Unit {
public:
/*
* Constructor. Parses the Dot11Beacon object and takes all the necessary
* data from it.
*/
Unit(Dot11Beacon& beacon);
Unit() = default;
unsigned getCount();
void incrementCount();
/*
* Prints this object onto the command line, in CSV format
*/
void print();
private:
string ssid;
string bssid;
unsigned channel;
unsigned count;
string gcs; // Group Cipher Suite
string pcs; // Pairwise Cipher Suite
string akm; // Authentication suite
/*
* Returns a string representation of a RSNInformation::CypherSuites enum value
*/
string type_to_string(const RSNInformation::CypherSuites& type);
/*
* Returns a string representation of a RSNInformation::AKMSuites enum value
*/
string type_to_string(const RSNInformation::AKMSuites& type);
};
Unit::Unit(Dot11Beacon& beacon) :
count {1} /* When this unit is created, it has been seen exactly once */ {
ssid = beacon.ssid();
bssid = beacon.addr3().to_string();
channel = unsigned(beacon.ds_parameter_set());
RSNInformation rsn;
for(const auto &opt : beacon.options()) {
if (opt.option() == Dot11::RSN) {
rsn = beacon.rsn_information();
// Put all authentication suite types in a string
const RSNInformation::akm_type& akmTypeList = rsn.akm_cyphers();
for (const auto& akmIt : akmTypeList) {
if (akm.size() == 0)
akm += type_to_string(akmIt);
else
akm += ";" + type_to_string(akmIt);
}
// Put all group cipher types in a string
const RSNInformation::CypherSuites& gcsType = rsn.group_suite();
gcs = type_to_string(gcsType);
// Put all pairwise ciphers in a string
const RSNInformation::cyphers_type& pcsTypeList = rsn.pairwise_cyphers();
for (const auto& pcsIt : pcsTypeList) {
if (pcs.size() == 0)
pcs += type_to_string(pcsIt);
else
pcs += ";" + type_to_string(pcsIt);
}
}
}
}
unsigned Unit::getCount() {
return count;
}
void Unit::incrementCount() {
count += 1;
}
void Unit::print() {
string ssid_to_print;
if (ssid.length() == 0) {
ssid_to_print = "<ZERO_LENGTH>";
} else if (!isprint(ssid[0])) {
ssid_to_print = to_string(static_cast<int>(ssid[0]));
} else {
ssid_to_print = ssid;
}
if (find(ssid_to_print.begin(), ssid_to_print.end(), ',') != ssid_to_print.end()) {
ssid_to_print = "\"" + ssid_to_print + "\"";
}
cout << ssid_to_print << ","
<< bssid << ","
<< to_string(channel) << ","
<< to_string(count) << ","
<< gcs << ","
<< pcs << ","
<< akm << endl;
}
string Unit::type_to_string(const RSNInformation::CypherSuites& type) {
switch (type) {
case RSNInformation::CypherSuites::CCMP:
return "CCMP";
break;
case RSNInformation::CypherSuites::TKIP:
return "TKIP";
break;
case RSNInformation::CypherSuites::WEP_104:
return "WEP_104";
break;
case RSNInformation::CypherSuites::WEP_40:
return "WEP_40";
break;
}
}
string Unit::type_to_string(const RSNInformation::AKMSuites& type) {
switch (type) {
case RSNInformation::AKMSuites::PMKSA:
return "PMKSA";
break;
case RSNInformation::AKMSuites::PSK:
return "PSK";
break;
}
}
/*
* Class that reads the pcap, keeps track of the units and writes out one
* beacon frame in pcap format for each unique AP it finds. This file is called
* "unique_beacons.pcap"
*/
class PCAPParser {
public:
/*
* Constructor. It takes the exact parameters that it will pas on to its
* FileSniffer object (a FileSniffer is actually just a file reader).
*/
PCAPParser(const string& pcapFilename, const string& filter);
/*
* Start reading the file.
*/
bool run();
/*
* Print CSV header and ask all of our collected Unit objects to print themselves
*/
void print();
private:
FileSniffer sniffer;
PacketWriter writer;
map<string, Unit> apMap; // stands for Access Point Map
bool handler(PDU&);
};
PCAPParser::PCAPParser(const string& pcapFilename, const string& filter) :
sniffer {pcapFilename, filter},
writer {"unique_beacons.pcap", PacketWriter::RADIOTAP} {
for (auto it = apMap.begin(); it != apMap.end(); it++) {
it->second.print();
}
}
bool PCAPParser::run() {
sniffer.sniff_loop( [this] (PDU& pdu) { return (bool) this->handler (pdu); } );
return true;
}
bool PCAPParser::handler(PDU& pdu) {
Dot11Beacon& beacon = pdu.rfind_pdu<Dot11Beacon>();
// An ESSID may span multiple BSSID's. Also, it's nice to keep track of what
// channels an AP has been on. Therefore, the combination of SSID, BSSID and
// channel is considered key.
const string& ssid = beacon.ssid();
const string& mac = beacon.addr3().to_string();
const unsigned channel = unsigned(beacon.ds_parameter_set());
const string key = ssid + mac + to_string(channel);
if (apMap.find(key) == apMap.end()) { // we've got a new one
Unit unit(beacon);
apMap[key] = unit;
writer.write(pdu);
} else {
apMap[key].incrementCount();
}
return true;
}
void PCAPParser::print() {
// Print the headers for the CSV output
cout << "SSID,BSSID,Current_channel,Count,Group_Cipher,Pairwise_Ciphers,Authentication_Suite" << endl;
// Ask each of the units to print themselves for the CSV output
for (auto it = apMap.begin(); it != apMap.end(); it++) {
it->second.print();
}
}
int main(int argc, char *argv[]) {
if(argc != 2) {
std::cout << "Usage: " << *argv << " <PCAP_FILE>\n";
return 1;
}
PCAPParser pcapParser(argv[1], "wlan type mgt subtype beacon");
pcapParser.run();
pcapParser.print();
}
Compile with:
g++ pcapreader.cpp -o pcapreader -O3 -std=c++11 -lpthread -ltins
The output is:
$ ./pcapreader capture.pcap
SSID,BSSID,Current_channel,Count,Group_Cipher, Pairwise_Ciphers,Authentication_Suite
MyWiFi,XX:XX:XX:XX:XX:XX,13,2,TKIP,TKIP;CCMP,PSK
...
...
Final note: if you open unique_beacons.pcap, you may find a lot of [Malformed Packet]. Apparently, a frame can still be successfully parsed if some of the tagged parameters are received wrongly. You could try to modify the code so that it only writes out frames to the pcap file that are completely intact.
tshark will print the "showname" attribute for text output only. You can control the output with a custom formatted by setting the gui.column.format preference. To print just SSID columns:
$ tshark -r capture.pcap -c 1 -T text -o 'gui.column.format:"SSID","%Cus:wlan.ssid"'

Resources