/* * Copyright 2016 by Ludovic Pouzenc * * Greatly inspired from msock.h written by Christian Beier */ #include #include #include #include "sockets.h" int mcast_recv_socket(char *mcast_ip, char *port, int wanted_so_rcvbuf) { int sock; struct addrinfo hints = { 0 }; /* Hints for name lookup */ struct addrinfo *ai_local = NULL; /* Local address to bind to */ struct addrinfo *mcast_ai = NULL; /* Multicast Address */ int yes=1; int status, optval; socklen_t optval_len; int dfltrcvbuf; /* Resolve the multicast group address */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_NUMERICHOST; if ((status = getaddrinfo(mcast_ip, NULL, &hints, &mcast_ai)) != 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 = mcast_ai->ai_family; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_PASSIVE; /* Return an address we can bind to */ if ( getaddrinfo(NULL, port, &hints, &ai_local) != 0 ) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); goto error; } /* Create socket for receiving datagrams */ if ( (sock = socket(ai_local->ai_family, ai_local->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, ai_local->ai_addr, ai_local->ai_addrlen) != 0 ) { perror("bind() failed"); goto error; } /* get/set socket receive buffer */ optval=0; optval_len = sizeof(optval); if(getsockopt(sock, SOL_SOCKET, SO_RCVBUF,(char*)&optval, &optval_len) !=0) { perror("getsockopt"); goto error; } dfltrcvbuf = optval; optval = wanted_so_rcvbuf; 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; } fprintf(stderr, "tried to set socket receive buffer from %d to %d, got %d\n", dfltrcvbuf, wanted_so_rcvbuf, optval); /* Join the multicast group. We do this seperately depending on whether we * are using IPv4 or IPv6. */ if ( mcast_ai->ai_family == PF_INET && mcast_ai->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*)(mcast_ai->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 ( mcast_ai->ai_family == PF_INET6 && mcast_ai->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*)(mcast_ai->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(ai_local) freeaddrinfo(ai_local); if(mcast_ai) freeaddrinfo(mcast_ai); return sock; error: if(ai_local) freeaddrinfo(ai_local); if(mcast_ai) freeaddrinfo(mcast_ai); return -1; } int mcast_send_socket(char* mcast_ip, char* port, int multicastTTL, struct addrinfo **mcast_ai) { int sock; struct addrinfo hints = { 0 }; /* Hints for name lookup */ int status; /* Resolve destination address for multicast datagrams */ hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_flags = AI_NUMERICHOST; if ((status = getaddrinfo(mcast_ip, port, &hints, mcast_ai)) != 0 ) { fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status)); return -1; } /* Create socket for sending multicast datagrams */ if ( (sock = socket((*mcast_ai)->ai_family, (*mcast_ai)->ai_socktype, 0)) < 0 ) { perror("socket() failed"); freeaddrinfo(*mcast_ai); return -1; } /* Set TTL of multicast packet */ if ( setsockopt(sock, (*mcast_ai)->ai_family == PF_INET6 ? IPPROTO_IPV6 : IPPROTO_IP, (*mcast_ai)->ai_family == PF_INET6 ? IPV6_MULTICAST_HOPS : IP_MULTICAST_TTL, (char*) &multicastTTL, sizeof(multicastTTL)) != 0 ) { perror("setsockopt() failed"); freeaddrinfo(*mcast_ai); return -1; } /* set the sending interface */ if((*mcast_ai)->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(*mcast_ai); return -1; } } if((*mcast_ai)->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(*mcast_ai); return -1; } } return sock; } int ucast_server_socket(char* port, int max_pending_conn) { int sock; struct addrinfo *serverAddr; struct addrinfo hints = { 0 }; /* Hints for name lookup */ int status; /* Prepare an addrinfo struct for a local socket */ hints.ai_family = PF_INET6; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if ((status = getaddrinfo(NULL, port, &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, max_pending_conn) < 0 ) { perror("listen() failed"); close(sock); } return sock; } int ucast_client_socket(char* server_ip, char* port) { int sock; struct addrinfo *serverAddr; struct addrinfo hints = { 0 }; /* Hints for name lookup */ int status; /* Resolve destination address */ hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST; if ((status = getaddrinfo(server_ip, port, &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; }