summaryrefslogtreecommitdiff
path: root/mcastseed/src
diff options
context:
space:
mode:
authorLudovic Pouzenc <ludovic@pouzenc.fr>2016-06-17 11:31:16 +0200
committerLudovic Pouzenc <ludovic@pouzenc.fr>2016-06-17 11:31:16 +0200
commitfbb8c27761964f3ee87f859bfbb57db75849cd1a (patch)
tree6ec5ed2a72e5e2205614ec96678c62aee3c869c4 /mcastseed/src
downloadeficast-fbb8c27761964f3ee87f859bfbb57db75849cd1a.tar.gz
eficast-fbb8c27761964f3ee87f859bfbb57db75849cd1a.tar.bz2
eficast-fbb8c27761964f3ee87f859bfbb57db75849cd1a.zip
Initial import : make-boot-image.sh operationnal. Dumb mcastseed.
Diffstat (limited to 'mcastseed/src')
-rw-r--r--mcastseed/src/Makefile.am12
-rw-r--r--mcastseed/src/mcastleech.c227
-rw-r--r--mcastseed/src/mcastseed.c421
-rw-r--r--mcastseed/src/msock.c371
-rw-r--r--mcastseed/src/msock.h74
5 files changed, 1105 insertions, 0 deletions
diff --git a/mcastseed/src/Makefile.am b/mcastseed/src/Makefile.am
new file mode 100644
index 0000000..b28166c
--- /dev/null
+++ b/mcastseed/src/Makefile.am
@@ -0,0 +1,12 @@
+## Process this file with automake to produce Makefile.in
+
+AM_CFLAGS =\
+ -Wall \
+ -g
+
+bin_PROGRAMS = mcastseed mcastleech
+
+mcastseed_SOURCES = mcastseed.c msock.c
+mcastleech_SOURCES = mcastleech.c msock.c
+
+LDADD = @WSOCKLIB@
diff --git a/mcastseed/src/mcastleech.c b/mcastseed/src/mcastleech.c
new file mode 100644
index 0000000..d19bff9
--- /dev/null
+++ b/mcastseed/src/mcastleech.c
@@ -0,0 +1,227 @@
+/* client.c
+ * Greatly inspired from examples written by tmouse, July 2005
+ * http://cboard.cprogramming.com/showthread.php?t=67469
+ * Modified to run multi-platform by Christian Beier <dontmind@freeshell.org>.
+ */
+
+#ifndef __MINGW32__
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "msock.h"
+
+#define MULTICAST_RECV_BUF 10240
+#define MULTICAST_SO_RCVBUF 425984
+#define DEFAULT_MCAST_IP_STR "ff02::114"
+#define DEFAULT_PORT_STR "9000"
+
+/* Cmdline Arguments */
+char *prog_name = NULL;
+char *mcast_ip = NULL;
+char *port = NULL;
+
+/* Sockets as global, used everywhere, even in die() */
+SOCKET mcast_sock = (SOCKET) -1; /* Multicast socket for receiving data */
+SOCKET ucast_sock = (SOCKET) -1; /* Unicast socket for give feedback to server */
+
+/* Buffer used for earch recvfrom() */
+char recvbuf[MULTICAST_RECV_BUF];
+
+/* Strings to print out representation of various states of the program */
+const char * const state_str[] = {
+ "exiting",
+ "wait_hello_and_connect_back",
+ "wait_start_and_start_job",
+ "receive_data",
+ "finalize_job",
+ "is_there_more_job"
+};
+
+/* Some boring funcs you didn't want to read now */
+void die(char* msg);
+void usage(char *msg);
+void arg_parse(int argc, char* argv[]);
+
+/* Parts of the "protocol", definitions are after main() */
+int wait_hello_and_connect_back();
+int wait_start_and_start_job();
+int receive_data();
+int finalize_job();
+int is_there_more_job();
+
+
+int main(int argc, char* argv[]) {
+ int state = 1; /* state of the "protocol" state machine */
+ int res;
+
+ arg_parse(argc, argv);
+
+ /* Finite state machine */
+ while ( state > 0 ) {
+ fprintf(stderr, "Now in %s state\n", state_str[state]);
+ switch ( state ) {
+ case 1: state = (wait_hello_and_connect_back() == 0)?2:1; break;
+ case 2: state = (wait_start_and_start_job() == 0)?2:3; break;
+ case 3: res = receive_data();
+ if (res==0) state = 4;
+ else if (res==1) state=3;
+ else state = -1;
+ break;
+ case 4: state = (finalize_job() == 0)?5:-2; break;
+ case 5: state = (is_there_more_job() == 0)?2:0; break;
+ }
+ }
+
+ if ( mcast_sock > 0 ) {
+ close(mcast_sock);
+ }
+
+ if ( state < 0 )
+ return -state;
+
+ return EXIT_SUCCESS;
+}
+
+
+int wait_hello_and_connect_back() {
+ /* Buffers for host and service strings after resolve */
+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+ /* Server address, filled by system after first recvfrom */
+ struct sockaddr_storage peer_addr;
+ socklen_t peer_addr_len;
+ /* Various needed variables */
+ ssize_t nread;
+ int res;
+
+ /* Setup mcast_sock */
+ if ( mcast_sock > 0 ) {
+ close(mcast_sock);
+ mcast_sock = 0;
+ }
+ mcast_sock = mcast_recv_socket(mcast_ip, port, MULTICAST_SO_RCVBUF);
+ if(mcast_sock < 0)
+ usage("Could not setup multicast socket. Wrong args given ?");
+
+ /* Wait for a single datagram from the server (for sync, no check on contain) */
+ peer_addr_len = sizeof(struct sockaddr_storage);
+ nread = recvfrom(mcast_sock, recvbuf, MULTICAST_RECV_BUF, 0, (struct sockaddr *) &peer_addr, &peer_addr_len);
+ if (nread < 0 ) {
+ perror("recvfrom() failed");
+ return -1;
+ }
+ /* Get peer informations as strings from peer_addr */
+ res = getnameinfo((struct sockaddr *) &peer_addr, peer_addr_len,
+ hbuf, NI_MAXHOST, sbuf, NI_MAXSERV, NI_NUMERICSERV);
+ if ( res != 0 ) {
+ fprintf(stderr, "getnameinfo: %s\n", gai_strerror(res));
+ return -2;
+ }
+ /* Connect back to the server, with reliable unicast */
+ if ( ucast_sock > 0 ) {
+ close(ucast_sock);
+ }
+ ucast_sock = ucast_client_socket(hbuf,port);
+ if(ucast_sock < 0) {
+ fprintf(stderr, "Could not setup unicast socket or connect to %s:%s\n", hbuf, port);
+ return -3;
+ }
+
+ return 0;
+}
+
+int wait_start_and_start_job() {
+ ssize_t nread, nwrite;
+
+ /* Wait for a "start" datagram from the server */
+ nread = recvfrom(mcast_sock, recvbuf, MULTICAST_RECV_BUF, 0, NULL, 0);
+ if (nread < 0 ) {
+ perror("recvfrom() failed");
+ return -1;
+ }
+ if ( nread >= 5 && strncmp("start", recvbuf, 5) == 0 ) {
+
+ nwrite = write(ucast_sock, "ready", 5);
+ if ( nwrite < 0 ) {
+ fprintf(stderr, "write() failed\n");
+ return -2;
+ }
+ if (nwrite != 5) {
+ fprintf(stderr, "write() short\n");
+ return -3;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int receive_data() {
+ ssize_t nread;
+ uint32_t seq;
+ uint16_t datalen;
+
+ /* Wait for a "dataN" datagram from the server */
+ nread = recvfrom(mcast_sock, recvbuf, MULTICAST_RECV_BUF, 0, NULL, 0);
+ if (nread < 0 ) {
+ perror("recvfrom() failed");
+ return -1;
+ }
+ if ( nread >= 10 && strncmp("data", recvbuf, 4) == 0 ) {
+ seq = ntohl( *( (uint32_t *) recvbuf+1 ) );
+ datalen = ntohs( *( (uint16_t *) recvbuf+4 ) );
+ //fprintf(stderr, "debug seq==%i, datalen==%hi\n", seq, datalen);
+ if ( nread != (10 + datalen) ) {
+ fprintf(stderr, "debug nread==%zi, (10 + datalen)==%i\n", nread, (10 + datalen));
+ //TODO nack ?
+ return -2;
+ }
+ fprintf(stdout, "data #%i, ", seq);
+ fwrite(recvbuf+10, datalen, 1, stdout);
+ fflush(stdout);
+ //TODO buffer zero copy, ack
+ return 1;
+ }
+
+ return 0;
+}
+
+int finalize_job() {
+ return 0;
+}
+int is_there_more_job() {
+ return 1;
+}
+
+
+
+
+void die(char* msg) {
+ fprintf(stderr, "%s\n", msg);
+ if (mcast_sock > 0)
+ close(mcast_sock);
+ if (ucast_sock > 0)
+ close(ucast_sock);
+ exit(EXIT_FAILURE);
+}
+
+void usage(char *msg) {
+ char ubuf[256];
+ if ( msg != NULL )
+ fprintf(stderr, "%s\n", msg);
+ ubuf[0] = '\0';
+ snprintf(ubuf, 255, "Usage: %s [port] [mcast_ip]\n", prog_name);
+ die(ubuf);
+}
+
+void arg_parse(int argc, char* argv[]) {
+ prog_name = argv[0];
+ if ( argc > 3 )
+ usage("Too many arguments");
+ port = (argc >= 2)?argv[1]:DEFAULT_PORT_STR;
+ mcast_ip = (argc >= 3)?argv[2]:DEFAULT_MCAST_IP_STR;
+}
+
diff --git a/mcastseed/src/mcastseed.c b/mcastseed/src/mcastseed.c
new file mode 100644
index 0000000..da73353
--- /dev/null
+++ b/mcastseed/src/mcastseed.c
@@ -0,0 +1,421 @@
+/* server.c
+ * Greatly inspired from examples written by tmouse, July 2005
+ * http://cboard.cprogramming.com/showthread.php?t=67469
+ * Modified to run multi-platform by Christian Beier <dontmind@freeshell.org>.
+ */
+
+#ifndef __MINGW32__
+#include <unistd.h> /* for usleep() */
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include "msock.h"
+
+#define READ_BUF_LEN 256
+#define MAX_PENDING_CONNECTIONS 256
+#define MAX_CLIENTS 256
+#define DEFAULT_MCAST_IP_STR "ff02::114"
+#define DEFAULT_PORT_STR "9000"
+#define DEFAULT_MCAST_TTL 1
+
+/* Cmdline Arguments */
+char *prog_name = NULL;
+char *mcast_ip = NULL;
+char *port = NULL;
+int mcast_ttl = 0;
+
+/* Sockets as global, used everywhere, even in die() */
+SOCKET mcast_sock = (SOCKET) -1; /* Multicast socket for sending data */
+SOCKET ucast_sock = (SOCKET) -1; /* Unicast socket for havee feedback from clients */
+
+/* Socket related data */
+struct addrinfo *mcast_addr = NULL;
+struct client {
+ SOCKET sock;
+ struct sockaddr addr;
+ int state;
+} clients[MAX_CLIENTS];
+int clients_next = 0;
+
+/* Buffer used for earch read() */
+char readbuf[READ_BUF_LEN];
+
+/* Strings to print out representation of various states of the program */
+const char * const state_str[] = {
+ "exiting",
+ "send_hello",
+ "accept_pending_clients_or_wait_a_bit",
+ "start_job",
+ "send_data",
+ "wait_all_finalize_job",
+ "is_there_more_job"
+};
+
+/* Some boring funcs you didn't want to read now */
+void die(char* msg);
+void usage(char *msg);
+void arg_parse(int argc, char* argv[]);
+void unsetup_sockets();
+void setup_sockets();
+
+/* Parts of the "protocol", definitions are after main() */
+int send_hello();
+int accept_pending_clients_or_wait_a_bit();
+int start_job();
+int send_data();
+int wait_all_finalize_job();
+int is_there_more_job();
+
+int main(int argc, char *argv[]) {
+ int state = 1; /* state of the "protocol" state machine */
+ int res;
+ arg_parse(argc, argv);
+ setup_sockets();
+
+ /* Finite state machine */
+ while ( state > 0 ) {
+ fprintf(stderr, "Now in %s state\n", state_str[state]);
+ switch ( state ) {
+ case 1: res = send_hello(); state = (res==0)?2:-1; break;
+ case 2: res = accept_pending_clients_or_wait_a_bit();
+ if (res==0) state = 2; // Some clients has just come in, try to get more
+ else if (res==1) state = 1; // Nothing new. Keep accepting clients after another hello
+ else if (res==2) state = 3; // Wanted clients are accepted
+ else state = -2;
+ break;
+ case 3: res = start_job();
+ if (res==0) state = 3; // Keep trying to convince every client to start
+ else if (res==1) state = 4; // All clients have started the job pipe
+ else if (res==2) state = 4; // There is dead clients but all alive are ready to go
+ else state = -3;
+ break;
+ case 4: res = send_data();
+ if (res==0) state = 4;
+ else if (res==1) state = 5; // All data sent
+ else state = -4;
+ break;
+ case 5: res = wait_all_finalize_job();
+ if (res==0) state = 5;
+ else if (res==1) state = 6;
+ else state = -5;
+ case 6: res = is_there_more_job();
+ if (res==0) state = 0;
+ else if (res==1) state = 3;
+ else state = -6;
+ break;
+ }
+ }
+
+ unsetup_sockets();
+
+ if ( state < 0 )
+ return -state;
+
+ return EXIT_SUCCESS;
+}
+
+int send_hello() {
+ ssize_t nwrite;
+ const char *payload = "hello";
+ int paylen = strlen(payload);
+
+ nwrite = sendto(mcast_sock, payload, paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+ if ( nwrite < 0 ) {
+ perror("sendto() failed");
+ return -1;
+ }
+ if ( nwrite < paylen ) {
+ fprintf(stderr, "%s", "Short packet sent");
+ }
+
+ return 0;
+}
+
+int accept_pending_clients_or_wait_a_bit() {
+ struct timeval timeout;
+ fd_set readfds, exceptfds;
+ ssize_t nread;
+ int res;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&exceptfds);
+ FD_SET(0,&readfds); // Read from stdin. Will never work as is on Windows, requires threads and so.
+ FD_SET(ucast_sock,&readfds);
+ FD_SET(ucast_sock,&exceptfds);
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+
+ res = select(ucast_sock+1, &readfds, NULL, &exceptfds, &timeout);
+ if ( res < 0 ) {
+ perror("select() failed");
+ return -1;
+ }
+
+ if ( res > 0 ) {
+ if (FD_ISSET(ucast_sock, &readfds)) {
+ //TODO : this assumes that the event is an accept() while ones could be send data there
+ if ( clients_next >= MAX_CLIENTS ) {
+ fprintf(stderr, "%s\n", "Bouncing client, MAX_CLIENTS reached");
+ close(accept(ucast_sock, NULL, 0));
+ } else {
+ socklen_t addrlen = sizeof(struct sockaddr);
+ clients[clients_next].sock = accept(ucast_sock, &(clients[clients_next].addr), &addrlen);
+ clients[clients_next].state = 0;
+ printf("Connected client on fd %i\n", clients[clients_next].sock);
+ clients_next++;
+ }
+ }
+ //TODO : drop this keybord read with accept(), this is not portable
+ if ( FD_ISSET(0, &readfds)) {
+ nread = read(0, readbuf, READ_BUF_LEN);
+ if ( nread <= 0 ) {
+ fprintf(stderr, "%s\n", "lost stdin");
+ }
+ // User wants to go now
+ return 2;
+ }
+ if (FD_ISSET(ucast_sock, &exceptfds)) {
+ fprintf(stderr, "%s\n", "unhandled except on ucast_sock");
+ return -2;
+ }
+ }
+ if (res == 0 ) {
+ // nothing happened before timeout
+ return 1;
+ }
+ return 0;
+}
+
+int start_job() {
+ struct timeval timeout;
+ fd_set readfds, exceptfds;
+ ssize_t nread, nwrite;
+ int all_ready, all_non_dead_ready;
+ int i, res;
+ SOCKET client_sock;
+ const char *payload = "start";
+ int paylen = strlen(payload);
+
+ nwrite = sendto(mcast_sock, payload, paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+ if ( nwrite < 0 ) {
+ perror("sendto() failed");
+ return -1;
+ }
+ if ( nwrite < paylen ) {
+ fprintf(stderr, "%s", "Short packet sent");
+ }
+
+ all_ready = 1;
+ all_non_dead_ready = 1;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&exceptfds);
+ for ( i=0; i<clients_next; i++) {
+ FD_SET(clients[i].sock,&readfds);
+ FD_SET(clients[i].sock,&exceptfds);
+ }
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+ res = select(clients_next, &readfds, NULL, &exceptfds, &timeout);
+ if ( res < 0 ) {
+ perror("select() failed");
+ return -1;
+ }
+
+ if ( res > 0 ) {
+ for ( i=0; i<clients_next; i++) {
+ client_sock = clients[i].sock;
+ if (FD_ISSET(client_sock, &readfds)) {
+ printf("todo info from client %i\n", i);
+ nread = read(client_sock, readbuf, 5);
+ if ( nread <= 0 ) {
+ fprintf(stderr, "lost client %i\n", i);
+ clients[i].state = 2;
+ } else if ( nread < 5 ) {
+ fprintf(stderr, "short data from %i\n", i);
+ clients[i].state = 2;
+ } else if ( strncmp("ready", readbuf, 5) != 0 ) {
+ fprintf(stderr, "unexpected data from %i\n", i);
+ clients[i].state = 2;
+ } else {
+ // Received "ready" ack from client
+ clients[i].state = 1;
+ }
+ }
+ if (FD_ISSET(clients[i].sock, &exceptfds)) {
+ fprintf(stderr, "unhandled except on client %i\n", i);
+ clients[i].state = 2;
+ }
+ all_ready &= (clients[i].state == 1);
+ if ( clients[i].state != 2)
+ all_non_dead_ready &= (clients[i].state == 1);
+ }
+ }
+ // (res == 0 ) nothing happened before timeout
+
+ if ( all_ready )
+ return 1;
+ if ( all_non_dead_ready )
+ return 2;
+
+ return 0;
+}
+
+
+int send_data() {
+ ssize_t nwrite;
+ char buf[] = "dataXXXXXXJe suis à la plage.";
+ int paylen = strlen(buf)-10;
+ int seq = 1;
+
+ //FIXME use http://troydhanson.github.io/tpl/index.html
+ *( (uint32_t *) buf+1 ) = htonl(seq);
+ *( (uint16_t *) buf+4 ) = htons(paylen);
+
+ nwrite = sendto(mcast_sock, buf, 10+paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+ if ( nwrite < 0 ) {
+ perror("sendto() failed");
+ return -1;
+ }
+ if ( nwrite < paylen ) {
+ fprintf(stderr, "%s", "Short packet sent");
+ }
+
+ return 1;
+}
+
+
+int wait_all_finalize_job() {
+ struct timeval timeout;
+ fd_set readfds, exceptfds;
+ ssize_t nread, nwrite;
+ int all_non_dead_done;
+ int i, res;
+ SOCKET client_sock;
+ const char *payload = "final";
+ int paylen = strlen(payload);
+
+ nwrite = sendto(mcast_sock, payload, paylen, 0, mcast_addr->ai_addr, mcast_addr->ai_addrlen);
+ if ( nwrite < 0 ) {
+ perror("sendto() failed");
+ return -1;
+ }
+ if ( nwrite < paylen ) {
+ fprintf(stderr, "%s", "Short packet sent");
+ }
+
+ all_non_dead_done = 1;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&exceptfds);
+ for ( i=0; i<clients_next; i++) {
+ FD_SET(clients[i].sock,&readfds);
+ FD_SET(clients[i].sock,&exceptfds);
+ }
+ timeout.tv_sec = 2;
+ timeout.tv_usec = 0;
+ res = select(clients_next, &readfds, NULL, &exceptfds, &timeout);
+ if ( res < 0 ) {
+ perror("select() failed");
+ return -1;
+ }
+
+ if ( res > 0 ) {
+ for ( i=0; i<clients_next; i++) {
+ client_sock = clients[i].sock;
+ if (FD_ISSET(client_sock, &readfds)) {
+ printf("todo info from client %i\n", i);
+ nread = read(client_sock, readbuf, 5);
+ if ( nread <= 0 ) {
+ fprintf(stderr, "lost client %i\n", i);
+ clients[i].state = 2;
+ } else if ( nread < 5 ) {
+ fprintf(stderr, "short data from %i\n", i);
+ clients[i].state = 2;
+ } else if ( strncmp("done.", readbuf, 5) != 0 ) {
+ fprintf(stderr, "unexpected data from %i\n", i);
+ clients[i].state = 2;
+ } else {
+ // Received "done." ack from client
+ clients[i].state = 3;
+ }
+ }
+ if (FD_ISSET(clients[i].sock, &exceptfds)) {
+ fprintf(stderr, "unhandled except on client %i\n", i);
+ clients[i].state = 2;
+ }
+ if ( clients[i].state != 2)
+ all_non_dead_done &= (clients[i].state == 3);
+ }
+ }
+ // (res == 0 ) nothing happened before timeout
+
+ if ( all_non_dead_done )
+ return 1;
+
+ return 0;
+}
+
+
+int is_there_more_job() {
+ return 0;
+}
+
+
+void die(char* msg) {
+ fprintf(stderr, "%s\n", msg);
+ if (mcast_sock > 0)
+ close(mcast_sock);
+ if (ucast_sock > 0)
+ close(ucast_sock);
+ exit(EXIT_FAILURE);
+}
+
+void usage(char *msg) {
+ char ubuf[256];
+ if ( msg != NULL )
+ fprintf(stderr, "%s\n", msg);
+ ubuf[0] = '\0';
+ snprintf(ubuf, 255, "Usage: %s [port] [mcast_ip] [mcast_ttl]\n", prog_name);
+ die(ubuf);
+}
+
+void arg_parse(int argc, char* argv[]) {
+ prog_name = argv[0];
+ if ( argc > 3 )
+ usage("Too many arguments");
+ port = (argc >= 2)?argv[1]:DEFAULT_PORT_STR;
+ mcast_ip = (argc >= 3)?argv[2]:DEFAULT_MCAST_IP_STR;
+ mcast_ttl = (argc >= 4)?atoi(argv[3]):DEFAULT_MCAST_TTL;
+ if ( mcast_ttl < 1 || mcast_ttl > 64 )
+ mcast_ttl = 1;
+}
+
+void unsetup_sockets() {
+ if ( ucast_sock > 0 ) {
+ close(ucast_sock);
+ ucast_sock = 0;
+ }
+
+ if ( mcast_sock > 0 ) {
+ close(mcast_sock);
+ mcast_sock = 0;
+ if ( mcast_addr ) {
+ freeaddrinfo(mcast_addr);
+ mcast_addr = 0;
+ }
+ }
+}
+
+void setup_sockets() {
+ /* Setup ucast_sock */
+ ucast_sock = ucast_server_socket(port, MAX_PENDING_CONNECTIONS);
+ if(ucast_sock < 0)
+ usage("Could not setup unicast socket. Wrong args given ?");
+
+ /* Setup mcast_sock */
+ mcast_sock = mcast_send_socket(mcast_ip, port, mcast_ttl, &mcast_addr);
+ if(mcast_sock < 0)
+ usage("Could not setup multicast socket. Wrong args given ?");
+}
+
diff --git a/mcastseed/src/msock.c b/mcastseed/src/msock.c
new file mode 100644
index 0000000..8274710
--- /dev/null
+++ b/mcastseed/src/msock.c
@@ -0,0 +1,371 @@
+/*
+ msock.c - multicast socket creation routines
+
+ (C) 2016 Christian Beier <dontmind@sdf.org>
+
+*/
+
+#ifndef __MINGW32__
+#include <unistd.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "msock.h"
+
+SOCKET mcast_send_socket(char* multicastIP, char* multicastPort, int multicastTTL, struct addrinfo **multicastAddr) {
+
+ SOCKET sock;
+ struct addrinfo hints = { 0 }; /* Hints for name lookup */
+
+
+#ifdef WIN32
+ WSADATA trash;
+ if(WSAStartup(MAKEWORD(2,0),&trash)!=0)
+ DieWithError("Couldn't init Windows Sockets\n");
+#endif
+
+
+ /*
+ Resolve destination address for multicast datagrams
+ */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ int status;
+ if ((status = getaddrinfo(multicastIP, multicastPort, &hints, multicastAddr)) != 0 )
+ {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ return -1;
+ }
+
+
+
+ /*
+ Create socket for sending multicast datagrams
+ */
+ if ( (sock = socket((*multicastAddr)->ai_family, (*multicastAddr)->ai_socktype, 0)) < 0 ) {
+ perror("socket() failed");
+ freeaddrinfo(*multicastAddr);
+ return -1;
+ }
+
+ /*
+ Set TTL of multicast packet
+ */
+ if ( setsockopt(sock,
+ (*multicastAddr)->ai_family == PF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP,
+ (*multicastAddr)->ai_family == PF_INET6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL,
+ (char*) &multicastTTL, sizeof(multicastTTL)) != 0 ) {
+ perror("setsockopt() failed");
+ freeaddrinfo(*multicastAddr);
+ return -1;
+ }
+
+
+ /*
+ set the sending interface
+ */
+ if((*multicastAddr)->ai_family == PF_INET) {
+ in_addr_t iface = INADDR_ANY; /* well, yeah, any */
+ if(setsockopt (sock,
+ IPPROTO_IP,
+ IP_MULTICAST_IF,
+ (char*)&iface, sizeof(iface)) != 0) {
+ perror("interface setsockopt() sending interface");
+ freeaddrinfo(*multicastAddr);
+ return -1;
+ }
+
+ }
+ if((*multicastAddr)->ai_family == PF_INET6) {
+ unsigned int ifindex = 0; /* 0 means 'default interface'*/
+ if(setsockopt (sock,
+ IPPROTO_IPV6,
+ IPV6_MULTICAST_IF,
+ (char*)&ifindex, sizeof(ifindex)) != 0) {
+ perror("interface setsockopt() sending interface");
+ freeaddrinfo(*multicastAddr);
+ return -1;
+ }
+
+ }
+
+
+ return sock;
+
+}
+
+
+
+SOCKET mcast_recv_socket(char* multicastIP, char* multicastPort, int multicastRecvBufSize) {
+
+ SOCKET sock;
+ struct addrinfo hints = { 0 }; /* Hints for name lookup */
+ struct addrinfo* localAddr = 0; /* Local address to bind to */
+ struct addrinfo* multicastAddr = 0; /* Multicast Address */
+ int yes=1;
+
+#ifdef WIN32
+ WSADATA trash;
+ if(WSAStartup(MAKEWORD(2,0),&trash)!=0)
+ DieWithError("Couldn't init Windows Sockets\n");
+#endif
+
+
+ /* Resolve the multicast group address */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+ int status;
+ if ((status = getaddrinfo(multicastIP, NULL, &hints, &multicastAddr)) != 0) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ goto error;
+ }
+
+
+ /*
+ Get a local address with the same family (IPv4 or IPv6) as our multicast group
+ This is for receiving on a certain port.
+ */
+ hints.ai_family = multicastAddr->ai_family;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE; /* Return an address we can bind to */
+ if ( getaddrinfo(NULL, multicastPort, &hints, &localAddr) != 0 ) {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ goto error;
+ }
+
+
+ /* Create socket for receiving datagrams */
+ if ( (sock = socket(localAddr->ai_family, localAddr->ai_socktype, 0)) < 0 ) {
+ perror("socket() failed");
+ goto error;
+ }
+
+
+
+ /*
+ * Enable SO_REUSEADDR to allow multiple instances of this
+ * application to receive copies of the multicast datagrams.
+ */
+ if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(int)) == -1) {
+ perror("setsockopt");
+ goto error;
+ }
+
+ /* Bind the local address to the multicast port */
+ if ( bind(sock, localAddr->ai_addr, localAddr->ai_addrlen) != 0 ) {
+ perror("bind() failed");
+ goto error;
+ }
+
+ /* get/set socket receive buffer */
+ int optval=0;
+ socklen_t optval_len = sizeof(optval);
+ int dfltrcvbuf;
+ if(getsockopt(sock, SOL_SOCKET, SO_RCVBUF,(char*)&optval, &optval_len) !=0) {
+ perror("getsockopt");
+ goto error;
+ }
+ dfltrcvbuf = optval;
+ optval = multicastRecvBufSize;
+ if(setsockopt(sock,SOL_SOCKET,SO_RCVBUF,(char*)&optval,sizeof(optval)) != 0) {
+ perror("setsockopt");
+ goto error;
+ }
+ if(getsockopt(sock, SOL_SOCKET, SO_RCVBUF,(char*)&optval, &optval_len) != 0) {
+ perror("getsockopt");
+ goto error;
+ }
+ printf("tried to set socket receive buffer from %d to %d, got %d\n",
+ dfltrcvbuf, multicastRecvBufSize, optval);
+
+
+
+
+ /* Join the multicast group. We do this seperately depending on whether we
+ * are using IPv4 or IPv6.
+ */
+ if ( multicastAddr->ai_family == PF_INET &&
+ multicastAddr->ai_addrlen == sizeof(struct sockaddr_in) ) /* IPv4 */
+ {
+ struct ip_mreq multicastRequest; /* Multicast address join structure */
+
+ /* Specify the multicast group */
+ memcpy(&multicastRequest.imr_multiaddr,
+ &((struct sockaddr_in*)(multicastAddr->ai_addr))->sin_addr,
+ sizeof(multicastRequest.imr_multiaddr));
+
+ /* Accept multicast from any interface */
+ multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY);
+
+ /* Join the multicast address */
+ if ( setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &multicastRequest, sizeof(multicastRequest)) != 0 ) {
+ perror("setsockopt() failed");
+ goto error;
+ }
+ }
+ else if ( multicastAddr->ai_family == PF_INET6 &&
+ multicastAddr->ai_addrlen == sizeof(struct sockaddr_in6) ) /* IPv6 */
+ {
+ struct ipv6_mreq multicastRequest; /* Multicast address join structure */
+
+ /* Specify the multicast group */
+ memcpy(&multicastRequest.ipv6mr_multiaddr,
+ &((struct sockaddr_in6*)(multicastAddr->ai_addr))->sin6_addr,
+ sizeof(multicastRequest.ipv6mr_multiaddr));
+
+ /* Accept multicast from any interface */
+ multicastRequest.ipv6mr_interface = 0;
+
+ /* Join the multicast address */
+ if ( setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, (char*) &multicastRequest, sizeof(multicastRequest)) != 0 ) {
+ perror("setsockopt() failed");
+ goto error;
+ }
+ }
+ else {
+ perror("Neither IPv4 or IPv6");
+ goto error;
+ }
+
+
+
+ if(localAddr)
+ freeaddrinfo(localAddr);
+ if(multicastAddr)
+ freeaddrinfo(multicastAddr);
+
+ return sock;
+
+ error:
+ if(localAddr)
+ freeaddrinfo(localAddr);
+ if(multicastAddr)
+ freeaddrinfo(multicastAddr);
+
+ return -1;
+}
+
+SOCKET ucast_client_socket(char* serverIP, char* serverPort) {
+
+ SOCKET sock;
+ struct addrinfo *serverAddr;
+ struct addrinfo hints = { 0 }; /* Hints for name lookup */
+
+
+#ifdef WIN32
+ WSADATA trash;
+ if(WSAStartup(MAKEWORD(2,0),&trash)!=0)
+ DieWithError("Couldn't init Windows Sockets\n");
+#endif
+
+
+ /*
+ Resolve destination address
+ */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICHOST;
+ int status;
+ if ((status = getaddrinfo(serverIP, serverPort, &hints, &serverAddr)) != 0 )
+ {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ return -1;
+ }
+
+ /*
+ Create socket
+ */
+ if ( (sock = socket(serverAddr->ai_family, serverAddr->ai_socktype, 0)) < 0 ) {
+ perror("socket() failed");
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+
+ /*
+ Connect it to the remote server
+ */
+ if ( connect(sock, serverAddr->ai_addr, serverAddr->ai_addrlen) < 0 ) {
+ perror("connect() failed");
+ close(sock);
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+
+ freeaddrinfo(serverAddr);
+ return sock;
+}
+
+SOCKET ucast_server_socket(char* serverPort, int maxPendingConnections) {
+
+ SOCKET sock;
+ struct addrinfo *serverAddr;
+ struct addrinfo hints = { 0 }; /* Hints for name lookup */
+
+
+#ifdef WIN32
+ WSADATA trash;
+ if(WSAStartup(MAKEWORD(2,0),&trash)!=0)
+ DieWithError("Couldn't init Windows Sockets\n");
+#endif
+
+
+ /*
+ Prepare an addrinfo struct for a local socket
+ */
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ int status;
+ if ((status = getaddrinfo(NULL, serverPort, &hints, &serverAddr)) != 0 )
+ {
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
+ return -1;
+ }
+ /*
+ Create socket
+ */
+ if ( (sock = socket(serverAddr->ai_family, serverAddr->ai_socktype, serverAddr->ai_protocol)) < 0 ) {
+ perror("socket() failed");
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+
+ /*
+ Accepts also IPv4 traffic if the socket is INET6
+ */
+ if(serverAddr->ai_family == PF_INET6) {
+ unsigned int no = 0;
+ if(setsockopt (sock,
+ IPPROTO_IPV6,
+ IPV6_V6ONLY,
+ (char*)&no, sizeof(no)) != 0) {
+ perror("setsockopt() !IPV6_V6ONLY failed");
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+ }
+
+ /*
+ Bind socket to local address/port
+ */
+ if ( bind(sock, serverAddr->ai_addr, serverAddr->ai_addrlen) < 0 ) {
+ perror("bind() failed");
+ close(sock);
+ freeaddrinfo(serverAddr);
+ return -1;
+ }
+
+ freeaddrinfo(serverAddr);
+ /*
+ Start listening incoming connections
+ */
+ if ( listen(sock, maxPendingConnections) < 0 ) {
+ perror("listen() failed");
+ close(sock);
+ }
+
+ return sock;
+}
diff --git a/mcastseed/src/msock.h b/mcastseed/src/msock.h
new file mode 100644
index 0000000..07e1fad
--- /dev/null
+++ b/mcastseed/src/msock.h
@@ -0,0 +1,74 @@
+/*
+ msock.h - multicast socket creation routines
+
+ (C) 2016 Christian Beier <dontmind@sdf.org>
+
+*/
+
+
+#ifndef MSOCK_H
+#define MSOCK_H
+
+/* this is for windows compat */
+#define SOCKET int
+
+#ifdef __MINGW32__
+#undef SOCKET
+#undef socklen_t
+#define WINVER 0x0501
+#include <ws2tcpip.h>
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define close closesocket
+#define socklen_t int
+typedef unsigned int in_addr_t;
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+
+/* Define IPV6_ADD_MEMBERSHIP for FreeBSD and Mac OS X */
+#ifndef IPV6_ADD_MEMBERSHIP
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#endif
+
+
+
+/**
+ Creates a socket suitable for sending multicast datagrams via sendto().
+
+ On success, a socket is returned and multicastAddr set with the right info for sendto() calls.
+ On error, -1 is returned.
+*/
+SOCKET mcast_send_socket(char* multicastIP, char* multicastPort, int multicastTTL, struct addrinfo **multicastAddr);
+
+/**
+ Creates a socket suitable for receiving multicast datagrams via recvfrom(). Also sets socket recv buffer.
+
+ On success, returns socket.
+ On fail, returns -1.
+ */
+SOCKET mcast_recv_socket(char* multicastIP, char* multicastPort, int multicastRecvBufSize);
+
+/**
+ Creates a TCP socket that connects to serverIP:serverPort.
+ Meant to give feedback to a mcast sender.
+
+ On success, returns socket.
+ On error, -1 is returned.
+ */
+SOCKET ucast_client_socket(char* serverIP, char* serverPort);
+
+/**
+ Creates a TCP socket that binds locally serverPort.
+ Meant to receive feedback to a mcast sender.
+
+ On success, returns socket.
+ On error, -1 is returned.
+ */
+SOCKET ucast_server_socket(char* serverPort, int maxPendingConnections);
+
+#endif