📄 net_udp.c
字号:
/*
* FILE: net_udp.c
* AUTHOR: Colin Perkins
* MODIFIED: Orion Hodson & Piers O'Hanlon
*
* Copyright (c) 1998-2000 University College London
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, is permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the Computer Science
* Department at University College London
* 4. Neither the name of the University nor of the Department may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* If this machine supports IPv6 the symbol HAVE_IPv6 should */
/* be defined in either config_unix.h or config_win32.h. The */
/* appropriate system header files should also be included */
/* by those files. */
#include "config_unix.h"
#include "config_win32.h"
#include "debug.h"
#include "memory.h"
#include "inet_pton.h"
#include "inet_ntop.h"
#include "vsnprintf.h"
#include "net_udp.h"
#ifdef NEED_ADDRINFO_H
#include "addrinfo.h"
#endif
#define IPv4 4
#define IPv6 6
#ifdef WIN2K_IPV6
const struct in6_addr in6addr_any = {IN6ADDR_ANY_INIT};
#endif
/* This is pretty nasty but it's the simplest way to get round */
/* the Detexis bug that means their MUSICA IPv6 stack uses */
/* IPPROTO_IP instead of IPPROTO_IPV6 in setsockopt calls */
/* We also need to define in6addr_any */
#ifdef MUSICA_IPV6
#define IPPROTO_IPV6 IPPROTO_IP
struct in6_addr in6addr_any = {IN6ADDR_ANY_INIT};
/* These DEF's are required as MUSICA's winsock6.h causes a clash with some of the
* standard ws2tcpip.h definitions (eg struct in_addr6).
* Note: winsock6.h defines AF_INET6 as 24 NOT 23 as in winsock2.h - I have left it
* set to the MUSICA value as this is used in some of their function calls.
*/
//#define AF_INET6 23
#define IP_MULTICAST_LOOP 11 /*set/get IP multicast loopback */
#define IP_MULTICAST_IF 9 /* set/get IP multicast i/f */
#define IP_MULTICAST_TTL 10 /* set/get IP multicast ttl */
#define IP_MULTICAST_LOOP 11 /*set/get IP multicast loopback */
#define IP_ADD_MEMBERSHIP 12 /* add an IP group membership */
#define IP_DROP_MEMBERSHIP 13/* drop an IP group membership */
#define IN6_IS_ADDR_UNSPECIFIED(a) (((a)->s6_addr32[0] == 0) && \
((a)->s6_addr32[1] == 0) && \
((a)->s6_addr32[2] == 0) && \
((a)->s6_addr32[3] == 0))
struct ip_mreq {
struct in_addr imr_multiaddr; /* IP multicast address of group */
struct in_addr imr_interface; /* local IP address of interface */
};
#endif
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif
struct _socket_udp {
int mode; /* IPv4 or IPv6 */
char *addr;
uint16_t rx_port;
uint16_t tx_port;
ttl_t ttl;
fd_t fd;
struct in_addr addr4;
#ifdef HAVE_IPv6
struct in6_addr addr6;
#endif /* HAVE_IPv6 */
};
#ifdef WIN32
/* Want to use both Winsock 1 and 2 socket options, but since
* IPv6 support requires Winsock 2 we have to add own backwards
* compatibility for Winsock 1.
*/
#define SETSOCKOPT winsock_versions_setsockopt
#else
#define SETSOCKOPT setsockopt
#endif /* WIN32 */
/*****************************************************************************/
/* Support functions... */
/*****************************************************************************/
static void
socket_error(const char *msg, ...)
{
char buffer[255];
uint32_t blen = sizeof(buffer) / sizeof(buffer[0]);
va_list ap;
#ifdef WIN32
#define WSERR(x) {#x,x}
struct wse {
char errname[20];
int my_errno;
};
struct wse ws_errs[] = {
WSERR(WSANOTINITIALISED), WSERR(WSAENETDOWN), WSERR(WSAEACCES),
WSERR(WSAEINVAL), WSERR(WSAEINTR), WSERR(WSAEINPROGRESS),
WSERR(WSAEFAULT), WSERR(WSAENETRESET), WSERR(WSAENOBUFS),
WSERR(WSAENOTCONN), WSERR(WSAENOTSOCK), WSERR(WSAEOPNOTSUPP),
WSERR(WSAESHUTDOWN), WSERR(WSAEWOULDBLOCK), WSERR(WSAEMSGSIZE),
WSERR(WSAEHOSTUNREACH), WSERR(WSAECONNABORTED), WSERR(WSAECONNRESET),
WSERR(WSAEADDRNOTAVAIL), WSERR(WSAEAFNOSUPPORT), WSERR(WSAEDESTADDRREQ),
WSERR(WSAENETUNREACH), WSERR(WSAETIMEDOUT), WSERR(0)
};
int i, e = WSAGetLastError();
i = 0;
while(ws_errs[i].my_errno && ws_errs[i].my_errno != e) {
i++;
}
va_start(ap, msg);
_vsnprintf(buffer, blen, msg, ap);
va_end(ap);
printf("ERROR: %s, (%d - %s)\n", msg, e, ws_errs[i].errname);
#else
uint32_t retlen;
va_start(ap, msg);
retlen = vsnprintf(buffer, blen, msg, ap);
va_end(ap);
blen -= retlen;
snprintf(buffer + retlen, blen, ":%s", strerror(errno));
rtp_message(LOG_ALERT, buffer);
#endif
}
#ifdef WIN32
/* ws2tcpip.h defines these constants with different values from
* winsock.h so files that use winsock 2 values but try to use
* winsock 1 fail. So what was the motivation in changing the
* constants ?
*/
#define WS1_IP_MULTICAST_IF 2 /* set/get IP multicast interface */
#define WS1_IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */
#define WS1_IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */
#define WS1_IP_ADD_MEMBERSHIP 5 /* add an IP group membership */
#define WS1_IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */
/* winsock_versions_setsockopt tries 1 winsock version of option
* optname and then winsock 2 version if that failed.
*/
static int
winsock_versions_setsockopt(SOCKET s, int level, int optname, const char FAR * optval, int optlen)
{
int success = -1;
switch (optname) {
case IP_MULTICAST_IF:
success = setsockopt(s, level, WS1_IP_MULTICAST_IF, optval, optlen);
break;
case IP_MULTICAST_TTL:
success = setsockopt(s, level, WS1_IP_MULTICAST_TTL, optval, optlen);
break;
case IP_MULTICAST_LOOP:
success = setsockopt(s, level, WS1_IP_MULTICAST_LOOP, optval, optlen);
break;
case IP_ADD_MEMBERSHIP:
success = setsockopt(s, level, WS1_IP_ADD_MEMBERSHIP, optval, optlen);
break;
case IP_DROP_MEMBERSHIP:
success = setsockopt(s, level, WS1_IP_DROP_MEMBERSHIP, optval, optlen);
break;
}
if (success != -1) {
return success;
}
return setsockopt(s, level, optname, optval, optlen);
}
#endif
#ifdef NEED_INET_ATON
#ifdef NEED_INET_ATON_STATIC
static
#endif
int inet_aton(const char *name, struct in_addr *addr)
{
addr->s_addr = inet_addr(name);
return (addr->s_addr != (in_addr_t) INADDR_NONE);
}
#endif
#ifdef NEED_IN6_IS_ADDR_MULTICAST
#define IN6_IS_ADDR_MULTICAST(addr) ((addr)->s6_addr[0] == 0xffU)
#endif
#if defined(NEED_IN6_IS_ADDR_UNSPECIFIED) && defined(MUSICA_IPV6)
#define IN6_IS_ADDR_UNSPECIFIED(addr) IS_UNSPEC_IN6_ADDR(*addr)
#endif
/*****************************************************************************/
/* IPv4 specific functions... */
/*****************************************************************************/
static int udp_addr_valid4(const char *dst)
{
struct in_addr addr4;
struct hostent *h;
if (inet_pton(AF_INET, dst, &addr4)) {
return TRUE;
}
h = gethostbyname(dst);
if (h != NULL) {
return TRUE;
}
socket_error("Can't resolve IP address for %s", dst);
return FALSE;
}
static socket_udp *udp_init4(const char *addr, const char *iface, uint16_t rx_port, uint16_t tx_port, int ttl)
{
int reuse = 1;
struct sockaddr_in s_in;
struct in_addr iface_addr;
#ifdef WIN32
int recv_buf_size = 65536;
#endif
socket_udp *s = (socket_udp *)malloc(sizeof(socket_udp));
s->mode = IPv4;
s->addr = NULL;
s->rx_port = rx_port;
s->tx_port = tx_port;
s->ttl = ttl;
if (inet_pton(AF_INET, addr, &s->addr4) != 1) {
struct hostent *h = gethostbyname(addr);
if (h == NULL) {
socket_error("Can't resolve IP address for %s", addr);
free(s);
return NULL;
}
memcpy(&(s->addr4), h->h_addr_list[0], sizeof(s->addr4));
}
if (iface != NULL) {
if (inet_pton(AF_INET, iface, &iface_addr) != 1) {
rtp_message(LOG_ERR, "Illegal interface specification");
free(s);
return NULL;
}
} else {
iface_addr.s_addr = 0;
}
s->fd = socket(AF_INET, SOCK_DGRAM, 0);
if (s->fd < 0) {
socket_error("socket");
return NULL;
}
#ifdef WIN32
if (SETSOCKOPT(s->fd, SOL_SOCKET, SO_RCVBUF, (char *)&recv_buf_size, sizeof(int)) != 0) {
socket_error("setsockopt SO_RCVBUF");
return NULL;
}
#endif
if (SETSOCKOPT(s->fd, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse, sizeof(reuse)) != 0) {
socket_error("setsockopt SO_REUSEADDR");
return NULL;
}
#ifdef SO_REUSEPORT
if (SETSOCKOPT(s->fd, SOL_SOCKET, SO_REUSEPORT, (char *) &reuse, sizeof(reuse)) != 0) {
socket_error("setsockopt SO_REUSEPORT");
return NULL;
}
#endif
s_in.sin_family = AF_INET;
s_in.sin_addr.s_addr = INADDR_ANY;
s_in.sin_port = htons(rx_port);
if (bind(s->fd, (struct sockaddr *) &s_in, sizeof(s_in)) != 0) {
socket_error("bind: port %d", rx_port);
return NULL;
}
if (IN_MULTICAST(ntohl(s->addr4.s_addr))) {
char loop = 1;
struct ip_mreq imr;
imr.imr_multiaddr.s_addr = s->addr4.s_addr;
imr.imr_interface.s_addr = iface_addr.s_addr;
if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq)) != 0) {
socket_error("setsockopt IP_ADD_MEMBERSHIP");
return NULL;
}
#ifndef WIN32
if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) != 0) {
socket_error("setsockopt IP_MULTICAST_LOOP");
return NULL;
}
#endif
if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &s->ttl, sizeof(s->ttl)) != 0) {
socket_error("setsockopt IP_MULTICAST_TTL");
return NULL;
}
if (iface_addr.s_addr != 0) {
if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_MULTICAST_IF, (char *) &iface_addr, sizeof(iface_addr)) != 0) {
socket_error("setsockopt IP_MULTICAST_IF");
return NULL;
}
}
}
s->addr = strdup(addr);
return s;
}
static void udp_exit4(socket_udp *s)
{
if (IN_MULTICAST(ntohl(s->addr4.s_addr))) {
struct ip_mreq imr;
imr.imr_multiaddr.s_addr = s->addr4.s_addr;
imr.imr_interface.s_addr = INADDR_ANY;
if (SETSOCKOPT(s->fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char *) &imr, sizeof(struct ip_mreq)) != 0) {
socket_error("setsockopt IP_DROP_MEMBERSHIP");
abort();
}
rtp_message(LOG_INFO, "Dropped membership of multicast group");
}
close(s->fd);
free(s->addr);
free(s);
}
static int udp_send4(socket_udp *s, uint8_t *buffer, int buflen)
{
struct sockaddr_in s_in;
ASSERT(s != NULL);
ASSERT(s->mode == IPv4);
ASSERT(buffer != NULL);
ASSERT(buflen > 0);
s_in.sin_family = AF_INET;
s_in.sin_addr.s_addr = s->addr4.s_addr;
s_in.sin_port = htons(s->tx_port);
return sendto(s->fd, buffer, buflen, 0, (struct sockaddr *) &s_in, sizeof(s_in));
}
#ifndef _WIN32
static int udp_send_iov4(socket_udp *s, struct iovec *iov, int count)
{
struct sockaddr_in s_in;
struct msghdr msg;
ASSERT(s != NULL);
ASSERT(s->mode == IPv4);
ASSERT(iov != NULL);
ASSERT(count > 0);
s_in.sin_family = AF_INET;
s_in.sin_addr.s_addr = s->addr4.s_addr;
s_in.sin_port = htons(s->tx_port);
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&s_in;
msg.msg_namelen = sizeof(s_in);
msg.msg_iov = iov;
msg.msg_iovlen = count;
return sendmsg(s->fd, &msg, 0);
}
#endif
static char *udp_host_addr4(void)
{
char hname[MAXHOSTNAMELEN];
struct hostent *hent;
struct in_addr iaddr;
if (gethostname(hname, MAXHOSTNAMELEN) != 0) {
rtp_message(LOG_ERR, "Cannot get hostname!");
abort();
}
hent = gethostbyname(hname);
if (hent == NULL) {
socket_error("Can't resolve IP address for %s", hname);
return NULL;
}
ASSERT(hent->h_addrtype == AF_INET);
memcpy(&iaddr.s_addr, hent->h_addr, sizeof(iaddr.s_addr));
strncpy(hname, inet_ntoa(iaddr), MAXHOSTNAMELEN);
return xstrdup(hname);
}
/*****************************************************************************/
/* IPv6 specific functions... */
/*****************************************************************************/
static int udp_addr_valid6(const char *dst)
{
#ifdef HAVE_IPv6
struct in6_addr addr6;
switch (inet_pton(AF_INET6, dst, &addr6)) {
case 1:
return TRUE;
break;
case 0:
return FALSE;
break;
case -1:
rtp_message(LOG_ERR, "inet_pton failed");
errno = 0;
}
#endif /* HAVE_IPv6 */
UNUSED(dst);
return FALSE;
}
static socket_udp *udp_init6(const char *addr, const char *iface, uint16_t rx_port, uint16_t tx_port, int ttl)
{
#ifdef HAVE_IPv6
int reuse = 1;
struct sockaddr_in6 s_in;
socket_udp *s = (socket_udp *) malloc(sizeof(socket_udp));
s->mode = IPv6;
s->addr = NULL;
s->rx_port = rx_port;
s->tx_port = tx_port;
s->ttl = ttl;
if (iface != NULL) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -