Skip to content

Instantly share code, notes, and snippets.

@s-u
Created October 5, 2018 02:04
Show Gist options
  • Save s-u/b1684b3173d5b2859f187c5860b25de8 to your computer and use it in GitHub Desktop.
Save s-u/b1684b3173d5b2859f187c5860b25de8 to your computer and use it in GitHub Desktop.
UDP broadcast discovery
/* minimalistic UDP-based broadcast discovery tool
(C)2018 Simon Urbanek <simon.urbanek@R-project.org>
License: MIT
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static char buf[512];
static int active = 1;
const char *nomatch = "";
int main(int ac, char **av) {
int verb = 0;
int port = -1;
int prefix = 0;
int maxat = 8;
int UDPs = socket(AF_INET,SOCK_DGRAM,0);
int reuse=1;
if (setsockopt(UDPs, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) /* allow reuse */
perror("WARNING: Cannot activate socket addres re-usage for UDP");
if (setsockopt(UDPs, SOL_SOCKET, SO_BROADCAST, &reuse, sizeof(reuse)))
perror("WARNING: Cannot activate broadcast for UDP");
const char *req = 0;
int i = 0;
while (++i < ac)
if (av[i][0] == '-') switch(av[i][1]) {
case 'v':
verb++;
break;
case 'h':
printf("\n Usage: %s [-p=<port>] [-s] [-c=<query-content>] [-m=<max-attempts>] [<key>=<value> [...]]\n\n Default server port is 9552, default client port is 9551.\n If no query is specified server mode is assumed.\n\n",
av[0]);
return 0;
case 'p':
if (av[i][2] == '=')
port = atoi(av[i] + 3);
else {
fprintf(stderr, "ERROR: missing port in -p=<port>\n");
return 1;
}
break;
case 's':
prefix = 1;
break;
case 'm':
if (av[i][2] == '=')
maxat = atoi(av[i] + 3);
else {
fprintf(stderr, "ERROR: missing value in -m=<max.attempts>\n");
return 1;
}
break;
case 'c':
if (av[i][2] == '=')
req = av[i] + 3;
else {
fprintf(stderr, "ERROR: missing content in -c=<content>\n");
return 1;
}
break;
default:
fprintf(stderr, "WARNING: unsupported option %s\n", av[i]);
}
if (port < 1)
port = req ? 9551 : 9552;
struct sockaddr_in UDPsrv, peer;
memset(&UDPsrv,0,sizeof(UDPsrv));
UDPsrv.sin_family = AF_INET;
UDPsrv.sin_port = htons(port);
UDPsrv.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(UDPs, (struct sockaddr*)&UDPsrv, sizeof(UDPsrv))) {
perror("Can't bind UDP server");
return 1;
}
if (req) {
socklen_t len = sizeof(peer);
memset(&peer, 0, sizeof(UDPsrv));
peer.sin_family = AF_INET;
peer.sin_port = htons(9552);
peer.sin_addr.s_addr = htonl(INADDR_ANY); // inet_addr("127.0.0.1");
int sn = (int) sendto(UDPs, req, strlen(req), 0, (const struct sockaddr*) &peer, len);
if (verb)
printf("INFO: sendto returned %d\n", sn);
if (sn < 0)
perror("ERROR: send failed");
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 250000;
if (setsockopt(UDPs, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
perror("Cannot set UDP receive timeout");
return 1;
}
}
while (active) {
socklen_t len = sizeof(peer);
int n = recvfrom(UDPs, buf, sizeof(buf) - 1, 0, (struct sockaddr*) &peer, &len);
if (verb)
printf("%d\n", n);
if (n >= 0) {
buf[n] = 0;
if (verb)
printf("%x:%d \"%s\"\n", peer.sin_addr.s_addr, ntohs(peer.sin_port), buf);
if (req) {
if (prefix)
printf("%s:", inet_ntoa(peer.sin_addr));
puts(buf);
break;
}
int answ = 0;
i = 0;
while (++i < ac) {
if (strncmp(av[i], buf, n) == 0 &&
av[i][n] == '=') {
int sn = (int) sendto(UDPs, av[i] + n + 1, strlen(av[i] + n + 1),
0, (const struct sockaddr*) &peer, len);
if (sn < 0)
perror("Failed to send (valid) response");
answ = 1;
}
}
} else if (req) {
maxat--;
if (maxat < 1) {
fprintf(stderr, "ERROR: maximum number of attempts exhausted, giving up\n");
return 1;
}
int sn = (int) sendto(UDPs, req, strlen(req), 0, (const struct sockaddr*) &peer, len);
if (verb)
printf("INFO: sendto returned %d\n", sn);
if (sn < 0)
perror("ERROR: send failed");
}
fflush(stdout);
}
close(UDPs);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment