ESP32 WebServer: How to get ip address of incoming clients - webserver

With the help of this tutorial i created a simple WebServer on my ESP32:
void setup() {
Serial.begin(115200);
// Connect to Wi-Fi network with SSID and password
WiFi.softAP(ssid, password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP); //IP address of my ESP32
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects
Serial.println("New Client"); // print a message out in the serial port
//...
}
}
How to get the ip addess of this connecting client?^^ So thats maybe something there like
String ipaddress = /*...*/;

Function remoteIP returns the IP address as IPAddress object.
IPAddress ip = client.remoteIP();
IPAddress implements Printable so it can be used with println.
Serial.println(ip);
If you insist on getting the IP as String, the ESP32 core version of IPAddress has toString method.
String ipaddress = ip.toString();
There is no function to print the IPAddress object to characters array. You could use CStringBuilder class from my StreamLib to print the IPAddress to characters array. CStringBuilder allows to fill characters array with Arduino Stream class functions. The StreamLib library is in Library Manager.
If you don't want to use the StreamLib library here is a (maybe not ideal) function to format the IPAddress to characters array.
void ip2str(const IPAddress& ip, char* s) {
itoa(ip[0], s, 10);
size_t l = strlen(s);
s[l] = '.';
itoa(ip[1], s + l + 1, 10);
l = strlen(s);
s[l] = '.';
itoa(ip[2], s + l + 1, 10);
l = strlen(s);
s[l] = '.';
itoa(ip[3], s + l + 1, 10);
}

Related

Sending UDP datagrams from Arduino with EtherCard library

I am currently trying to send UDP datagrams from my Arduino to a server. I researched a bit online and found some code, which helped me to send them via a domain. Which works fine. But in my case I need to send it to a IP address, instead of a domain name (because my server doesn't have a domain). In this case I use the Ethercard library.
#include <EtherCard.h>
static byte mymac[] = { 0x1A,0x2B,0x3C,0x4D,0x5E,0x6F };
byte Ethernet::buffer[700];
static uint32_t timer;
const char website[] PROGMEM = "name.tld";
const int dstPort PROGMEM = 54321;
const int srcPort PROGMEM = 54321;
void setup () {
Serial.begin(9600);
// Change 'SS' to your Slave Select pin, if you arn't using the default pin
if (ether.begin(sizeof Ethernet::buffer, mymac, SS) == 0)
Serial.println( "Failed to access Ethernet controller");
if (!ether.dhcpSetup())
Serial.println("DHCP failed");
ether.printIp("IP: ", ether.myip);
ether.printIp("GW: ", ether.gwip);
ether.printIp("DNS: ", ether.dnsip);
//if (!ether.dnsLookup(website))
// Serial.println("DNS failed");
ether.printIp("SRV: ", ether.hisip);
}
char textToSend[] = "Hello";
void loop () {
if (millis() > timer) {
timer = millis() + 5000;
//static void sendUdp (char *data,uint8_t len,uint16_t sport, uint8_t *dip, uint16_t dport);
ether.sendUdp(textToSend, sizeof(textToSend), srcPort, ether.hisip, dstPort );
}
}
This is my code for sending something to a domain.
I assume you are using this library.
https://github.com/njh/EtherCard
The README explains how to send UDP datagrams. You want to use uint8_t parseIp(uint8_t *bytestr, const char *str) to format a destination IP address.
char payload[] = "My UDP message";
uint8_t nSourcePort = 1234;
uint8_t nDestinationPort = 5678;
uint8_t ipDestinationAddress[IP_LEN];
ether.parseIp(ipDestinationAddress, "192.168.0.200");
ether.sendUdp(payload, sizeof(payload), nSourcePort, ipDestinationAddress, nDestinationPort);

Retrieve server IP address using gethostbyname()

I'm trying to retrieve the server IP address using gethostbyname(required from me)
here is the part of the client application for connecting to the server
struct hostent *SN = gethostbyname(argv[1]);
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portNum);
server_addr.sin_addr.s_addr = htonl(*SN->h_addr); //I have a doubt about this line
int serverfd=0;
serverfd = connect(SocketD, (struct sockaddr*) &server_addr,
sizeof(server_addr));
where argv[1] is the name of the server, currently localhost.
I tried also this line of code
server_addr.sin_addr = *(struct in_addr*)SN->h_addr;
Could anyone please tell me what is wrong with my code?

Get ip address of client from the server

I'm trying to get the ip address of each of my clients that connect to my server. I save this into fields of a struct which I sent to a thread. I'm noticing that sometimes I get the right ip and sometimes the wrong one. My first peer to connect usually has an incorrect ip...
The problem is that inet_ntoa() returns a pointer to static memory that is overwritten each time you call inet_ntoa(). You need to make a copy of the data before calling inet_ntoa() again:
struct peerInfo{
char ip[16];
int socket;
};
while((newsockfd = accept(sockfd,(struct sockaddr *)&clt_addr, &addrlen)) > 0)
{
struct peerInfo *p = (struct peerInfo *) malloc(sizeof(struct peerInfo));
strncpy(p->ip, inet_ntoa(clt_addr.sin_addr), 16);
p->socket = newsockfd;
printf("A peer connection was accepted from %s:%hu\n", p->ip, ntohs(clt_addr.sin_port));
if (pthread_create(&thread_id , NULL, peer_handler, (void*)p) < 0)
{
syserr("could not create thread\n");
free(p);
return 1;
}
printf("Thread created for the peer.\n");
pthread_detach(thread_id);
}
if (newsockfd < 0)
{
syserr("Accept failed.\n");
}
From http://linux.die.net/man/3/inet_ntoa:
The inet_ntoa() function converts the Internet host address in, given
in network byte order, to a string in IPv4 dotted-decimal notation.
The string is returned in a statically allocated buffer, which
subsequent calls will overwrite.
Emphasis added.

Raw socket multicasting

I have a raw socket I have bound to eth2.
#define DEVICE_NAME "eth2"
// open a socket
int Socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (Socket < 0)
{
perror("socket() error");
return -1;
}
// create a interface request structure
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
// set the interface name
strncpy(ifr.ifr_name, DEVICE_NAME, IFNAMSIZ);
// get interface index
ioctl(Socket, SIOCGIFINDEX, &ifr);
int Socket_Index = ifr.ifr_ifindex;
// bind the socket to the interface
struct sockaddr_ll Socket_Addr;
Socket_Addr.sll_family = AF_PACKET;
Socket_Addr.sll_protocol = htons(ETH_P_ALL);
Socket_Addr.sll_ifindex = Socket_Index;
bind(Socket, (struct sockaddr *)&Socket_Addr, sizeof(Socket_Addr));
// add multicast addresses to the socket, based on Unit Number
struct packet_mreq mreq;
mreq.mr_ifindex = Socket_Index;
mreq.mr_type = PACKET_MR_MULTICAST;
mreq.mr_alen = ETH_ALEN;
memcpy(mreq.mr_address, Addresses[UNITS_1_2], ETH_ALEN);
setsockopt(Socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
memcpy(mreq.mr_address, Addresses[UNIT_3], ETH_ALEN);
setsockopt(Socket, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
Where Addresses[UNITS_1_2] resolves to 03:00:00:01:04:00 and Addresses[UNIT_3] resolves to 02:00:00:01:04:01.
The socket is only receiving the multicast packets, and not the unicast ones. While debugging I started tcpdump and low-and-behold going to promiscuous mode did the trick.
My question is: Can I receive both multicast and unicast packets on the same raw socket without promiscuous mode? I have tried adding 02:00:00:01:04:01 to eth0s mac addresses using maddr, with no luck.
Sneaking from gabhijit: Try adding
Socket_Addr.sll_pkttype = PACKET_HOST | PACKET_MULTICAST;

pcapdotnet in windows7 how to sniffer a port without specify a device or with the ip 0.0.0.0?

I have 2 software working together through the port 8888 in one computer, I want to know how they works. It's really nice if I can get another way, like software to do this job:)
I download the pcapdotnet and try the sample code on http://pcapdotnet.codeplex.com/wikipage?title=Pcap.Net%20User%20Guide&referringTitle=Home
It can got all messages on the local network but not for me.
I use netstat -a get this " TCP 0.0.0.0:8888 ZC01N00278:0 LISTENING"
I'm really confusing about this 0.0.0.0.
so I disable all my network device(this cause my pcap can't work, cause it need at least one device), but it still there. I suppose the 2 software communication each other without a Ethernet, is it true?
I am a newbie in socket, in which way I can get the packet in this port?
Here's the code, it mostly from the tutorial sample.
using System;
using System.Collections.Generic;
using PcapDotNet.Core;
using PcapDotNet.Packets;
using PcapDotNet.Packets.IpV4;
using PcapDotNet.Packets.Transport;
using System.IO;
namespace pcap_test1
{
class Program
{
static StreamWriter sw;
static void Main(string[] args)
{
sw = new StreamWriter(#"C:\sunxin\pcap.txt");
// Retrieve the device list from the local machine
IList<LivePacketDevice> allDevices = LivePacketDevice.AllLocalMachine;
if (allDevices.Count == 0)
{
Console.WriteLine("No interfaces found! Make sure WinPcap is installed.");
return;
}
// Print the list
for (int i = 0; i != allDevices.Count; ++i)
{
LivePacketDevice device = allDevices[i];
Console.Write((i + 1) + ". " + device.Name);
if (device.Description != null)
Console.WriteLine(" (" + device.Description + ")");
else
Console.WriteLine(" (No description available)");
}
int deviceIndex = 0;
do
{
Console.WriteLine("Enter the interface number (1-" + allDevices.Count + "):");
string deviceIndexString = Console.ReadLine();
if (!int.TryParse(deviceIndexString, out deviceIndex) ||
deviceIndex < 1 || deviceIndex > allDevices.Count)
{
deviceIndex = 0;
}
} while (deviceIndex == 0);
// Take the selected adapter
PacketDevice selectedDevice = allDevices[deviceIndex - 1];
// Open the device
using (PacketCommunicator communicator =
selectedDevice.Open(65536, // portion of the packet to capture
// 65536 guarantees that the whole packet will be captured on all the link layers
PacketDeviceOpenAttributes.Promiscuous, // promiscuous mode
1000)) // read timeout
{
// Check the link layer. We support only Ethernet for simplicity.
if (communicator.DataLink.Kind != DataLinkKind.Ethernet)
{
Console.WriteLine("This program works only on Ethernet networks.");
return;
}
// Compile the filter
using (BerkeleyPacketFilter filter = communicator.CreateFilter("port 8888"))
{
// Set the filter
communicator.SetFilter(filter);
}
Console.WriteLine("Listening on " + selectedDevice.Description + "...");
// start the capture
communicator.ReceivePackets(0, PacketHandler);
}
}
// Callback function invoked by libpcap for every incoming packet
private static void PacketHandler(Packet packet)
{
// print timestamp and length of the packet
Console.WriteLine(packet.Timestamp.ToString("yyyy-MM-dd hh:mm:ss.fff") + " length:" + packet.Ethernet);
sw.WriteLine(packet.Timestamp.ToString("yyyy-MM-dd hh:mm:ss.fff") + packet.Ethernet);
IpV4Datagram ip = packet.Ethernet.IpV4;
UdpDatagram udp = ip.Udp;
for (int i = ip.HeaderLength; i < packet.Length; ++i)
{
Console.Write(Convert.ToChar(packet.Buffer[i]));
sw.Write(Convert.ToChar(packet.Buffer[i]));
}
Console.WriteLine();
sw.WriteLine();
// print ip addresses and udp ports
//Console.WriteLine(ip.Source + ":" + udp.SourcePort + " -> " + ip.Destination + ":" + udp.DestinationPort);
//sw.WriteLine(ip.Source + ":" + udp.SourcePort + " -> " + ip.Destination + ":" + udp.DestinationPort);
sw.Flush();
}
}
}
Wireshark's wiki tells that WinPcap cannot capture packets between endpoints on the same computer in Windows (Pcap.Net uses WinPcap). It recommends to use RawCap.