Skip to content

Instantly share code, notes, and snippets.

@karstenBriksoft
Created March 5, 2024 17:01
Show Gist options
  • Save karstenBriksoft/459d678b7716a8f8e9bb1cb7ce211375 to your computer and use it in GitHub Desktop.
Save karstenBriksoft/459d678b7716a8f8e9bb1cb7ce211375 to your computer and use it in GitHub Desktop.
example of how to do a UDP broadcast request for finding local Samba sharing servers.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/_types/_socklen_t.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/types.h>
// based on the example from https://github.com/pyang30/linux-udp-broadcast-example/tree/master
// adapted to macOS 14.3
// WINS works on Port 137
#define PORT 137
// this is an example packet that sends a request for the WORKGROUP servers
char packet[] = {0x06, 0x51, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x46, 0x48, 0x45, 0x50,
0x46, 0x43, 0x45, 0x4C, 0x45, 0x48, 0x46, 0x43, 0x45, 0x50, 0x46, 0x46, 0x46, 0x41, 0x43, 0x41, 0x43,
0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x43, 0x41, 0x41, 0x41, 0x00, 0x00, 0x20, 0x00, 0x01};
size_t packetSize = sizeof(packet);
int main()
{
int sock;
int yes = 1;
struct sockaddr_in broadcast_addr;
struct sockaddr_in server_addr;
socklen_t addr_len;
int count;
int ret;
fd_set readfd;
uint8_t buffer[1024];
int i;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
perror("sock error");
return -1;
}
ret = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&yes, sizeof(yes));
if (ret == -1)
{
perror("setsockopt error");
return -1;
}
addr_len = sizeof(struct sockaddr_in);
memset((void*)&broadcast_addr, 0, addr_len);
broadcast_addr.sin_family = AF_INET;
broadcast_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
broadcast_addr.sin_port = htons(PORT);
ret = sendto(sock, packet, packetSize, 0, (struct sockaddr*)&broadcast_addr, addr_len);
if (ret == -1)
{
perror("failed to send");
return -1;
}
FD_ZERO(&readfd);
FD_SET(sock, &readfd);
struct timeval timeout = {0};
timeout.tv_usec = 50000;
for (i = 0; i < 30; i++)
{
ret = select(sock + 1, &readfd, NULL, NULL, &timeout);
if (ret > 0)
{
if (FD_ISSET(sock, &readfd))
{
count = recvfrom(sock, buffer, 1024, 0, (struct sockaddr*)&server_addr, &addr_len);
if (addr_len > 0)
{
// it may not be a wrong assumption to just take this address, it's from a server at least
uint8_t* addr = (uint8_t*)&(server_addr.sin_addr.s_addr);
printf("answer from: %i.%i.%i.%i\n", addr[0], addr[1], addr[2], addr[3]);
}
printf("\treceived %i bytes\n\t\tIP: ", count);
// the answer of a WINS package contains the IP-Address in its last 4 bytes
// this may be identical to the information found in server_addr (printed already)
for (int i = count - 4; i < count; i++)
{
unsigned b = buffer[i];
printf("%02i", b);
if (i == count - 1)
{
printf("\n");
}
else
{
printf(".");
}
}
}
}
else
{
// no more sockets to read from?
break;
}
}
}
@tempelmann
Copy link

tempelmann commented Mar 6, 2024

If you add the following after the call to setsockopt, then you can also broadcast on other than the default interface, and then the IP addr extracted may indeed be different from the one printed first, i.e. the first IP would be the one from which the host responds and the other one is the one where its primary interface is on.

	// special handling added by TT: bind to a specific interface (instead of using the default interface)
	struct addrinfo *targetInfo;
	char *localAddress = "192.168.61.119";
	ret = getaddrinfo (localAddress, NULL, NULL, &targetInfo);
	if (ret == -1) {
		perror("getaddrinfo error");
		return -1;
	}
	ret = bind (sock, targetInfo->ai_addr, targetInfo->ai_addrlen);
	if (ret == -1) {
		perror("bind error");
		return -1;
	}

Also, that 1024 in the recvfrom call should be changed to sizeof(buffer).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment