Squashed 'third_party/rawrtc/re/' content from commit f3163ce8b
Change-Id: I6a235e6ac0f03269d951026f9d195da05c40fdab
git-subtree-dir: third_party/rawrtc/re
git-subtree-split: f3163ce8b526a13b35ef71ce4dd6f43585064d8a
diff --git a/src/net/bsd/brt.c b/src/net/bsd/brt.c
new file mode 100644
index 0000000..d30aab4
--- /dev/null
+++ b/src/net/bsd/brt.c
@@ -0,0 +1,101 @@
+/**
+ * @file bsd/brt.c BSD routing table code
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mem.h>
+#include <re_sa.h>
+#include <re_net.h>
+#include <sys/sysctl.h>
+#include <net/route.h>
+#include <net/if.h>
+
+
+/*
+ * See https://github.com/boundary/libdnet/blob/master/src/route-bsd.c
+ */
+
+#ifdef __APPLE__
+#define RT_MSGHDR_ALIGNMENT sizeof(uint32_t)
+#else
+#define RT_MSGHDR_ALIGNMENT sizeof(unsigned long)
+#endif
+
+#define ROUNDUP(a) \
+ ((a) > 0 \
+ ? (1 + (((size_t)(a) - 1) | (RT_MSGHDR_ALIGNMENT - 1))) \
+ : RT_MSGHDR_ALIGNMENT)
+
+
+int net_rt_list(net_rt_h *rth, void *arg)
+{
+ /* net.route.0.inet.flags.gateway */
+ int mib[] = {CTL_NET, PF_ROUTE, 0, AF_UNSPEC,
+ NET_RT_FLAGS, RTF_GATEWAY};
+ char ifname[IFNAMSIZ], *buf, *p;
+ struct rt_msghdr *rt;
+ struct sockaddr *sa, *sa_tab[RTAX_MAX];
+ struct sa dst, gw;
+ size_t l;
+ int i, err = 0;
+
+ if (sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0)
+ return errno;
+ if (!l)
+ return ENOENT;
+
+ buf = mem_alloc(l, NULL);
+ if (!buf)
+ return ENOMEM;
+
+ if (sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) {
+ err = errno;
+ goto out;
+ }
+
+ for (p = buf; p<buf+l; p += rt->rtm_msglen) {
+ rt = (void *)p; /* buffer is aligned */
+ sa = (struct sockaddr *)(rt + 1);
+
+ if (rt->rtm_type != RTM_GET)
+ continue;
+
+ if (!(rt->rtm_flags & RTF_UP))
+ continue;
+
+ for (i=0; i<RTAX_MAX; i++) {
+
+ if (rt->rtm_addrs & (1 << i)) {
+ sa_tab[i] = sa;
+ sa = (struct sockaddr *)
+ ((char *)sa + ROUNDUP(sa->sa_len));
+ }
+ else {
+ sa_tab[i] = NULL;
+ }
+ }
+
+ if ((rt->rtm_addrs & RTA_DST) == RTA_DST) {
+ err = sa_set_sa(&dst, sa_tab[RTAX_DST]);
+ if (err)
+ continue;
+ }
+ if ((rt->rtm_addrs & RTA_GATEWAY) == RTA_GATEWAY) {
+ err = sa_set_sa(&gw, sa_tab[RTAX_GATEWAY]);
+ if (err)
+ continue;
+ }
+
+ if_indextoname(rt->rtm_index, ifname);
+
+ if (rth(ifname, &dst, 0, &gw, arg))
+ break;
+ }
+
+ out:
+ mem_deref(buf);
+
+ return err;
+}
diff --git a/src/net/if.c b/src/net/if.c
new file mode 100644
index 0000000..ed99cbf
--- /dev/null
+++ b/src/net/if.c
@@ -0,0 +1,233 @@
+/**
+ * @file net/if.c Network interface code
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_net.h>
+
+
+#define DEBUG_MODULE "netif"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/** Interface address entry */
+struct ifentry {
+ int af; /**< Address family */
+ char *ifname; /**< Interface name */
+ struct sa *ip; /**< IP address */
+ size_t sz; /**< Size of buffer */
+ bool found; /**< Found flag */
+};
+
+
+static bool if_getname_handler(const char *ifname, const struct sa *sa,
+ void *arg)
+{
+ struct ifentry *ife = arg;
+
+ if (ife->af != sa_af(sa))
+ return false;
+
+ if (sa_cmp(sa, ife->ip, SA_ADDR)) {
+ str_ncpy(ife->ifname, ifname, ife->sz);
+ ife->found = true;
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Get the name of the interface for a given IP address
+ *
+ * @param ifname Buffer for returned network interface name
+ * @param sz Size of buffer
+ * @param af Address Family
+ * @param ip Given IP address
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_if_getname(char *ifname, size_t sz, int af, const struct sa *ip)
+{
+ struct ifentry ife;
+ int err;
+
+ if (!ifname || !sz || !ip)
+ return EINVAL;
+
+ ife.af = af;
+ ife.ifname = ifname;
+ ife.ip = (struct sa *)ip;
+ ife.sz = sz;
+ ife.found = false;
+
+ err = net_if_list(if_getname_handler, &ife);
+
+ return ife.found ? err : ENODEV;
+}
+
+
+static bool if_getaddr_handler(const char *ifname,
+ const struct sa *sa, void *arg)
+{
+ struct ifentry *ife = arg;
+
+ /* Match name of interface? */
+ if (str_isset(ife->ifname) && 0 != str_casecmp(ife->ifname, ifname))
+ return false;
+
+ if (!sa_isset(sa, SA_ADDR))
+ return false;
+
+#if 1
+ /* skip loopback and link-local IP */
+ if (sa_is_loopback(sa) || sa_is_linklocal(sa))
+ return false;
+#endif
+
+ /* Match address family */
+ if (ife->af != sa_af(sa))
+ return false;
+
+ /* Match - copy address */
+ sa_cpy(ife->ip, sa);
+ ife->found = true;
+
+ return ife->found;
+}
+
+
+/**
+ * Get IP address for a given network interface
+ *
+ * @param ifname Network interface name (optional)
+ * @param af Address Family
+ * @param ip Returned IP address
+ *
+ * @return 0 if success, otherwise errorcode
+ *
+ * @deprecated Works for IPv4 only
+ */
+int net_if_getaddr(const char *ifname, int af, struct sa *ip)
+{
+ struct ifentry ife;
+ int err;
+
+ if (!ip)
+ return EINVAL;
+
+ ife.af = af;
+ ife.ifname = (char *)ifname;
+ ife.ip = ip;
+ ife.sz = 0;
+ ife.found = false;
+
+#ifdef HAVE_GETIFADDRS
+ err = net_getifaddrs(if_getaddr_handler, &ife);
+#else
+ err = net_if_list(if_getaddr_handler, &ife);
+#endif
+
+ return ife.found ? err : ENODEV;
+}
+
+
+static bool if_debug_handler(const char *ifname, const struct sa *sa,
+ void *arg)
+{
+ struct re_printf *pf = arg;
+
+ (void)re_hprintf(pf, " %10s: %j\n", ifname, sa);
+
+ return false;
+}
+
+
+/**
+ * Debug network interfaces
+ *
+ * @param pf Print handler for debug output
+ * @param unused Unused parameter
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_if_debug(struct re_printf *pf, void *unused)
+{
+ int err;
+
+ (void)unused;
+
+ err = re_hprintf(pf, "net interfaces:\n");
+
+#ifdef HAVE_GETIFADDRS
+ err |= net_getifaddrs(if_debug_handler, pf);
+#else
+ err |= net_if_list(if_debug_handler, pf);
+#endif
+
+ return err;
+}
+
+
+static bool linklocal_handler(const char *ifname, const struct sa *sa,
+ void *arg)
+{
+ void **argv = arg;
+ int af = *(int *)argv[1];
+
+ if (argv[0] && 0 != str_casecmp(argv[0], ifname))
+ return false;
+
+ if (af != AF_UNSPEC && af != sa_af(sa))
+ return false;
+
+ if (sa_is_linklocal(sa)) {
+ *((struct sa *)argv[2]) = *sa;
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Get the Link-local address for a specific network interface
+ *
+ * @param ifname Name of the interface
+ * @param af Address family
+ * @param ip Returned link-local address
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_if_getlinklocal(const char *ifname, int af, struct sa *ip)
+{
+ struct sa addr;
+ void *argv[3];
+ int err;
+
+ if (!ip)
+ return EINVAL;
+
+ sa_init(&addr, sa_af(ip));
+
+ argv[0] = (void *)ifname;
+ argv[1] = ⁡
+ argv[2] = &addr;
+
+ err = net_if_apply(linklocal_handler, argv);
+ if (err)
+ return err;
+
+ if (!sa_isset(&addr, SA_ADDR))
+ return ENOENT;
+
+ *ip = addr;
+
+ return 0;
+}
diff --git a/src/net/ifaddrs.c b/src/net/ifaddrs.c
new file mode 100644
index 0000000..2116bda
--- /dev/null
+++ b/src/net/ifaddrs.c
@@ -0,0 +1,64 @@
+/**
+ * @file ifaddrs.c Network interface code using getifaddrs().
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <unistd.h>
+#include <sys/socket.h>
+#define __USE_MISC 1 /**< Use MISC code */
+#include <net/if.h>
+#include <ifaddrs.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_sa.h>
+#include <re_net.h>
+
+
+#define DEBUG_MODULE "ifaddrs"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/**
+ * Get a list of all network interfaces including name and IP address.
+ * Both IPv4 and IPv6 are supported.
+ *
+ * @param ifh Interface handler, called once per network interface.
+ * @param arg Handler argument.
+ *
+ * @return 0 if success, otherwise errorcode.
+ */
+int net_getifaddrs(net_ifaddr_h *ifh, void *arg)
+{
+ struct ifaddrs *ifa, *ifp;
+ int err;
+
+ if (!ifh)
+ return EINVAL;
+
+ if (0 != getifaddrs(&ifa)) {
+ err = errno;
+ DEBUG_WARNING("getifaddrs: %m\n", err);
+ return err;
+ }
+
+ for (ifp = ifa; ifa; ifa = ifa->ifa_next) {
+ struct sa sa;
+
+ DEBUG_INFO("ifaddr: %10s flags=%08x\n", ifa->ifa_name,
+ ifa->ifa_flags);
+
+ if (ifa->ifa_flags & IFF_UP) {
+ err = sa_set_sa(&sa, ifa->ifa_addr);
+ if (err)
+ continue;
+
+ if (ifh(ifa->ifa_name, &sa, arg))
+ break;
+ }
+ }
+
+ freeifaddrs(ifp);
+
+ return 0;
+}
diff --git a/src/net/linux/rt.c b/src/net/linux/rt.c
new file mode 100644
index 0000000..2152af1
--- /dev/null
+++ b/src/net/linux/rt.c
@@ -0,0 +1,253 @@
+/**
+ * @file linux/rt.c Routing table code for Linux. See rtnetlink(7)
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#define _BSD_SOURCE 1
+#define _DEFAULT_SOURCE 1
+#include <string.h>
+#include <unistd.h>
+#define __USE_POSIX 1 /**< Use POSIX flag */
+#include <netdb.h>
+#define __USE_MISC 1
+#include <net/if.h>
+#undef __STRICT_ANSI__
+#include <linux/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <re_types.h>
+#include <re_mbuf.h>
+#include <re_fmt.h>
+#include <re_sa.h>
+#include <re_net.h>
+
+
+#define DEBUG_MODULE "linuxrt"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/* Override macros to avoid casting alignment warning */
+#undef RTM_RTA
+#define RTM_RTA(r) (void *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg)))
+#undef RTA_NEXT
+#define RTA_NEXT(rta, len) ((len) -= RTA_ALIGN((rta)->rta_len), \
+ (void *)(((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
+#undef NLMSG_NEXT
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+ (void*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+
+
+enum {BUFSIZE = 8192};
+
+
+/** Defines a network route */
+struct net_rt {
+ char ifname[IFNAMSIZ]; /**< Interface name */
+ struct sa dst; /**< Destination IP address/network */
+ int dstlen; /**< Prefix length of destination */
+ struct sa gw; /**< Gateway IP address */
+};
+
+
+static int read_sock(int fd, uint8_t *buf, size_t size, int seq, int pid)
+{
+ struct nlmsghdr *nlhdr;
+ int n = 0, len = 0;
+
+ do {
+ /* Receive response from the kernel */
+ if ((n = recv(fd, buf, size - len, 0)) < 0) {
+ DEBUG_WARNING("SOCK READ: %m\n", errno);
+ return -1;
+ }
+ nlhdr = (struct nlmsghdr *)(void *)buf;
+
+ /* Check if the header is valid */
+ if (0 == NLMSG_OK(nlhdr, (uint32_t)n) ||
+ NLMSG_ERROR == nlhdr->nlmsg_type) {
+ DEBUG_WARNING("Error in received packet\n");
+ return -1;
+ }
+
+ /* Check if the its the last message */
+ if (NLMSG_DONE == nlhdr->nlmsg_type) {
+ break;
+ }
+ else{
+ /* Else move the pointer to buffer appropriately */
+ buf += n;
+ len += n;
+ }
+
+ /* Check if its a multi part message */
+ if (0 == (nlhdr->nlmsg_flags & NLM_F_MULTI)) {
+ /* return if its not */
+ break;
+ }
+ } while (nlhdr->nlmsg_seq != (uint32_t)seq ||
+ nlhdr->nlmsg_pid != (uint32_t)pid);
+
+ return len;
+}
+
+
+/* Parse one route */
+static int rt_parse(const struct nlmsghdr *nlhdr, struct net_rt *rt)
+{
+ struct rtmsg *rtmsg;
+ struct rtattr *rtattr;
+ int len;
+
+ rtmsg = (struct rtmsg *)NLMSG_DATA(nlhdr);
+
+ /* If the route does not belong to main routing table then return. */
+ if (RT_TABLE_MAIN != rtmsg->rtm_table)
+ return EINVAL;
+
+ sa_init(&rt->dst, rtmsg->rtm_family);
+ rt->dstlen = rtmsg->rtm_dst_len;
+
+ /* get the rtattr field */
+ rtattr = (struct rtattr *)RTM_RTA(rtmsg);
+ len = RTM_PAYLOAD(nlhdr);
+ for (;RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) {
+
+ switch (rtattr->rta_type) {
+
+ case RTA_OIF:
+ if_indextoname(*(int *)RTA_DATA(rtattr), rt->ifname);
+ break;
+
+ case RTA_GATEWAY:
+ switch (rtmsg->rtm_family) {
+
+ case AF_INET:
+ sa_init(&rt->gw, AF_INET);
+ rt->gw.u.in.sin_addr.s_addr
+ = *(uint32_t *)RTA_DATA(rtattr);
+ break;
+
+#ifdef HAVE_INET6
+ case AF_INET6:
+ sa_set_in6(&rt->gw, RTA_DATA(rtattr), 0);
+ break;
+#endif
+
+ default:
+ DEBUG_WARNING("RTA_DST: unknown family %d\n",
+ rtmsg->rtm_family);
+ break;
+ }
+ break;
+
+#if 0
+ case RTA_PREFSRC:
+ rt->srcaddr = *(uint32_t *)RTA_DATA(rtattr);
+ break;
+#endif
+
+ case RTA_DST:
+ switch (rtmsg->rtm_family) {
+
+ case AF_INET:
+ sa_init(&rt->dst, AF_INET);
+ rt->dst.u.in.sin_addr.s_addr
+ = *(uint32_t *)RTA_DATA(rtattr);
+ break;
+
+#ifdef HAVE_INET6
+ case AF_INET6:
+ sa_set_in6(&rt->dst, RTA_DATA(rtattr), 0);
+ break;
+#endif
+
+ default:
+ DEBUG_WARNING("RTA_DST: unknown family %d\n",
+ rtmsg->rtm_family);
+ break;
+ }
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * List all entries in the routing table
+ *
+ * @param rth Route entry handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_rt_list(net_rt_h *rth, void *arg)
+{
+ union {
+ uint8_t buf[BUFSIZE];
+ struct nlmsghdr msg[1];
+ } u;
+ struct nlmsghdr *nlmsg;
+ int sock, len, seq = 0, err = 0;
+
+ if (!rth)
+ return EINVAL;
+
+ /* Create Socket */
+ if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) {
+ DEBUG_WARNING("list: socket(): (%m)\n", errno);
+ return errno;
+ }
+
+ /* Initialize the buffer */
+ memset(u.buf, 0, sizeof(u.buf));
+
+ /* point the header and the msg structure pointers into the buffer */
+ nlmsg = u.msg;
+
+ /* Fill in the nlmsg header*/
+ nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
+ nlmsg->nlmsg_type = RTM_GETROUTE;
+ nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+ nlmsg->nlmsg_seq = seq++;
+ nlmsg->nlmsg_pid = getpid();
+
+ /* Send the request */
+ if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
+ err = errno;
+ DEBUG_WARNING("list: write to socket failed (%m)\n", err);
+ goto out;
+ }
+
+ /* Read the response */
+ if ((len = read_sock(sock, u.buf, sizeof(u.buf), seq, getpid())) < 0) {
+ err = errno;
+ DEBUG_WARNING("list: read from socket failed (%m)\n", err);
+ goto out;
+ }
+
+ /* Parse and print the response */
+ for (;NLMSG_OK(nlmsg,(uint32_t)len);nlmsg = NLMSG_NEXT(nlmsg,len)) {
+ struct net_rt rt;
+
+ memset(&rt, 0, sizeof(struct net_rt));
+ if (0 != rt_parse(nlmsg, &rt))
+ continue;
+
+#ifdef HAVE_INET6
+ if (AF_INET6 == sa_af(&rt.dst)
+ && IN6_IS_ADDR_UNSPECIFIED(&rt.dst.u.in6.sin6_addr))
+ continue;
+#endif
+
+ if (rth(rt.ifname, &rt.dst, rt.dstlen, &rt.gw, arg))
+ break;
+ }
+
+ out:
+ (void)close(sock);
+
+ return err;
+}
diff --git a/src/net/mod.mk b/src/net/mod.mk
new file mode 100644
index 0000000..8f12899
--- /dev/null
+++ b/src/net/mod.mk
@@ -0,0 +1,41 @@
+#
+# mod.mk
+#
+# Copyright (C) 2010 Creytiv.com
+#
+
+# Generic files
+SRCS += net/if.c
+SRCS += net/net.c
+SRCS += net/netstr.c
+SRCS += net/rt.c
+SRCS += net/sock.c
+SRCS += net/sockopt.c
+
+
+# Platform dependant files
+ifneq ($(OS),win32)
+SRCS += net/posix/pif.c
+else
+SRCS += net/win32/wif.c
+endif
+
+
+# Routing
+ifeq ($(OS),linux)
+SRCS += net/linux/rt.c
+CFLAGS += -DHAVE_ROUTE_LIST
+else
+
+ifneq ($(HAVE_SYS_SYSCTL_H),)
+ifneq ($(HAVE_NET_ROUTE_H),)
+SRCS += net/bsd/brt.c
+CFLAGS += -DHAVE_ROUTE_LIST
+endif
+endif
+
+endif
+
+ifdef HAVE_GETIFADDRS
+SRCS += net/ifaddrs.c
+endif
diff --git a/src/net/net.c b/src/net/net.c
new file mode 100644
index 0000000..0978b1b
--- /dev/null
+++ b/src/net/net.c
@@ -0,0 +1,157 @@
+/**
+ * @file net.c Networking code.
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#define _BSD_SOURCE 1
+#define _DEFAULT_SOURCE 1
+#include <stdlib.h>
+#include <string.h>
+#if !defined(WIN32)
+#define __USE_BSD 1 /**< Use BSD code */
+#include <unistd.h>
+#include <netdb.h>
+#endif
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_net.h>
+
+
+#define DEBUG_MODULE "net"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/**
+ * Get the IP address of the host
+ *
+ * @param af Address Family
+ * @param ip Returned IP address
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_hostaddr(int af, struct sa *ip)
+{
+ char hostname[256];
+ struct in_addr in;
+ struct hostent *he;
+
+ if (-1 == gethostname(hostname, sizeof(hostname)))
+ return errno;
+
+ he = gethostbyname(hostname);
+ if (!he)
+ return ENOENT;
+
+ if (af != he->h_addrtype)
+ return EAFNOSUPPORT;
+
+ /* Get the first entry */
+ memcpy(&in, he->h_addr_list[0], sizeof(in));
+ sa_set_in(ip, ntohl(in.s_addr), 0);
+
+ return 0;
+}
+
+
+/**
+ * Get the default source IP address
+ *
+ * @param af Address Family
+ * @param ip Returned IP address
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_default_source_addr_get(int af, struct sa *ip)
+{
+#if defined(WIN32)
+ return net_hostaddr(af, ip);
+#else
+ char ifname[64] = "";
+
+#ifdef HAVE_ROUTE_LIST
+ /* Get interface with default route */
+ (void)net_rt_default_get(af, ifname, sizeof(ifname));
+#endif
+
+ /* First try with default interface */
+ if (0 == net_if_getaddr(ifname, af, ip))
+ return 0;
+
+ /* Then try first real IP */
+ if (0 == net_if_getaddr(NULL, af, ip))
+ return 0;
+
+ return net_if_getaddr4(ifname, af, ip);
+#endif
+}
+
+
+/**
+ * Get a list of all network interfaces including name and IP address.
+ * Both IPv4 and IPv6 are supported
+ *
+ * @param ifh Interface handler, called once per network interface
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_if_apply(net_ifaddr_h *ifh, void *arg)
+{
+#ifdef HAVE_GETIFADDRS
+ return net_getifaddrs(ifh, arg);
+#else
+ return net_if_list(ifh, arg);
+#endif
+}
+
+
+static bool net_rt_handler(const char *ifname, const struct sa *dst,
+ int dstlen, const struct sa *gw, void *arg)
+{
+ void **argv = arg;
+ struct sa *ip = argv[1];
+ (void)dst;
+ (void)dstlen;
+
+ if (0 == str_cmp(ifname, argv[0])) {
+ *ip = *gw;
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Get the IP-address of the default gateway
+ *
+ * @param af Address Family
+ * @param gw Returned Gateway address
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_default_gateway_get(int af, struct sa *gw)
+{
+ char ifname[64];
+ void *argv[2];
+ int err;
+
+ if (!af || !gw)
+ return EINVAL;
+
+ err = net_rt_default_get(af, ifname, sizeof(ifname));
+ if (err)
+ return err;
+
+ argv[0] = ifname;
+ argv[1] = gw;
+
+ err = net_rt_list(net_rt_handler, argv);
+ if (err)
+ return err;
+
+ return 0;
+}
diff --git a/src/net/netstr.c b/src/net/netstr.c
new file mode 100644
index 0000000..3f356d3
--- /dev/null
+++ b/src/net/netstr.c
@@ -0,0 +1,50 @@
+/**
+ * @file netstr.c Network strings
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_net.h>
+
+
+/**
+ * Get the name of a protocol
+ *
+ * @param proto Protocol
+ *
+ * @return Protocol name
+ */
+const char *net_proto2name(int proto)
+{
+ switch (proto) {
+
+ case IPPROTO_UDP: return "UDP";
+ case IPPROTO_TCP: return "TCP";
+#ifdef IPPROTO_SCTP
+ case IPPROTO_SCTP: return "SCTP";
+#endif
+ default: return "???";
+ }
+}
+
+
+/**
+ * Get the name of a address family
+ *
+ * @param af Address family
+ *
+ * @return Address family name
+ */
+const char *net_af2name(int af)
+{
+ switch (af) {
+
+ case AF_UNSPEC: return "AF_UNSPEC";
+ case AF_INET: return "AF_INET";
+#ifdef HAVE_INET6
+ case AF_INET6: return "AF_INET6";
+#endif
+ default: return "???";
+ }
+}
diff --git a/src/net/posix/pif.c b/src/net/posix/pif.c
new file mode 100644
index 0000000..17e93ab
--- /dev/null
+++ b/src/net/posix/pif.c
@@ -0,0 +1,167 @@
+/**
+ * @file posix/pif.c POSIX network interface code
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#define __USE_POSIX 1 /**< Use POSIX code */
+#define __USE_XOPEN2K 1/**< Use POSIX.1:2001 code */
+#include <netdb.h>
+#define __USE_MISC 1 /**< Use MISC code */
+#include <net/if.h>
+#include <arpa/inet.h>
+/*#include <net/if_arp.h>*/
+#ifdef __sun
+#include <sys/sockio.h>
+#endif
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_net.h>
+
+
+#define DEBUG_MODULE "posixif"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/**
+ * Get IP address for a given network interface
+ *
+ * @param ifname Network interface name
+ * @param af Address Family
+ * @param ip Returned IP address
+ *
+ * @return 0 if success, otherwise errorcode
+ *
+ * @deprecated Works for IPv4 only
+ */
+int net_if_getaddr4(const char *ifname, int af, struct sa *ip)
+{
+ struct addrinfo hints, *res, *r;
+ int error, err;
+
+ if (AF_INET != af)
+ return EAFNOSUPPORT;
+
+ memset(&hints, 0, sizeof(hints));
+ /* set-up hints structure */
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(NULL, "0", &hints, &res);
+ if (error) {
+ DEBUG_WARNING("get_ifaddr: getaddrinfo(): %s\n",
+ gai_strerror(error));
+ return EADDRNOTAVAIL;
+ }
+
+ err = ENOENT;
+ for (r = res; r; r = r->ai_next) {
+ struct ifreq ifrr;
+ int fd = -1;
+
+ fd = socket(r->ai_family, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ continue;
+ }
+
+ ifrr.ifr_addr.sa_family = r->ai_family;
+ str_ncpy(ifrr.ifr_name, ifname, sizeof(ifrr.ifr_name));
+
+ if (ioctl(fd, SIOCGIFADDR, &ifrr) < 0) {
+ err = errno;
+ goto next;
+ }
+
+ err = sa_set_sa(ip, &ifrr.ifr_ifru.ifru_addr);
+
+ next:
+ (void)close(fd);
+ }
+
+ freeaddrinfo(res);
+ return err;
+}
+
+
+/**
+ * Enumerate all network interfaces
+ *
+ * @param ifh Interface handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ *
+ * @deprecated Works for IPv4 only
+ */
+int net_if_list(net_ifaddr_h *ifh, void *arg)
+{
+ struct ifreq ifrv[32], *ifr;
+ struct ifconf ifc;
+ int sockfd = -1;
+ int err = 0;
+
+ if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) {
+ err = errno;
+ DEBUG_WARNING("interface list: socket(): (%m)\n", err);
+ goto out;
+ }
+
+ ifc.ifc_len = sizeof(ifrv);
+ ifc.ifc_req = ifrv;
+
+ if (0 != ioctl(sockfd, SIOCGIFCONF, &ifc)) {
+ err = errno;
+ DEBUG_WARNING("interface list: ioctl SIOCFIFCONF: %m\n", err);
+ goto out;
+ }
+
+ for (ifr = ifc.ifc_req;
+ (char *)ifr < ((char *)ifc.ifc_buf + ifc.ifc_len);
+ ++ifr) {
+ struct ifreq ifrr;
+ struct sa sa;
+
+ if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data)
+ continue; /* duplicate, skip it */
+
+ if (ioctl(sockfd, SIOCGIFFLAGS, ifr))
+ continue; /* failed to get flags, skip it */
+
+#if 0
+ if (ifr->ifr_flags & IFF_LOOPBACK)
+ continue;
+#endif
+
+ if (!(ifr->ifr_flags & IFF_UP))
+ continue;
+
+ ifrr.ifr_addr.sa_family = AF_INET;
+ str_ncpy(ifrr.ifr_name, ifr->ifr_name, sizeof(ifrr.ifr_name));
+
+ if (ioctl(sockfd, SIOCGIFADDR, &ifrr) < 0) {
+ err = errno;
+ continue;
+ }
+
+ err = sa_set_sa(&sa, &ifrr.ifr_ifru.ifru_addr);
+ if (err) {
+ DEBUG_WARNING("if_list: sa_set_sa %m\n", err);
+ break;
+ }
+
+ if (ifh && ifh(ifr->ifr_name, &sa, arg))
+ break;
+ }
+
+ out:
+ if (sockfd >= 0)
+ (void)close(sockfd);
+
+ return err;
+}
diff --git a/src/net/rt.c b/src/net/rt.c
new file mode 100644
index 0000000..f5d2ef5
--- /dev/null
+++ b/src/net/rt.c
@@ -0,0 +1,158 @@
+/**
+ * @file net/rt.c Generic routing table code
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#define _BSD_SOURCE 1
+#define _DEFAULT_SOURCE 1
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_sa.h>
+#include <re_net.h>
+
+
+struct net_rt {
+ int af;
+ char *ifname;
+ size_t size;
+ int prefix;
+};
+
+
+static bool rt_debug_handler(const char *ifname, const struct sa *dst,
+ int dstlen, const struct sa *gw, void *arg)
+{
+ char addr[64];
+ struct re_printf *pf = arg;
+ int err = 0;
+
+ (void)re_snprintf(addr, sizeof(addr), "%j/%d", dst, dstlen);
+
+ err |= re_hprintf(pf, " %-44s", addr);
+ err |= re_hprintf(pf, "%-40j", gw);
+ err |= re_hprintf(pf, " %-15s ", ifname);
+
+#ifdef HAVE_INET6
+ if (AF_INET6 == sa_af(dst)) {
+ const struct sockaddr_in6 *sin6 = &dst->u.in6;
+ const struct in6_addr *in6 = &sin6->sin6_addr;
+
+ if (IN6_IS_ADDR_MULTICAST(in6))
+ err |= re_hprintf(pf, " MULTICAST");
+ if (IN6_IS_ADDR_LINKLOCAL(in6))
+ err |= re_hprintf(pf, " LINKLOCAL");
+ if (IN6_IS_ADDR_SITELOCAL(in6))
+ err |= re_hprintf(pf, " SITELOCAL");
+ }
+#endif
+
+ err |= re_hprintf(pf, "\n");
+
+ return 0 != err;
+}
+
+
+/**
+ * Dump the routing table
+ *
+ * @param pf Print function for output
+ * @param unused Unused parameter
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_rt_debug(struct re_printf *pf, void *unused)
+{
+ int err = 0;
+
+ (void)unused;
+
+ err |= re_hprintf(pf, "net routes:\n");
+
+ err |= re_hprintf(pf, " Destination "
+ "Next Hop"
+ " Iface "
+ "Type\n");
+
+ err |= net_rt_list(rt_debug_handler, pf);
+
+ return err;
+}
+
+
+static bool rt_default_get_handler(const char *_ifname, const struct sa *dst,
+ int dstlen, const struct sa *gw, void *arg)
+{
+ struct net_rt *rt = arg;
+
+ (void)dstlen;
+ (void)gw;
+
+ if (sa_af(dst) != rt->af)
+ return false;
+
+ switch (rt->af) {
+
+ case AF_INET:
+ if (0 == sa_in(dst)) {
+ str_ncpy(rt->ifname, _ifname, rt->size);
+ return true;
+ }
+ break;
+
+#ifdef HAVE_INET6
+ case AF_INET6:
+ if (IN6_IS_ADDR_MULTICAST(&dst->u.in6.sin6_addr))
+ return false;
+ if (IN6_IS_ADDR_LINKLOCAL(&dst->u.in6.sin6_addr))
+ return false;
+
+ if (dstlen < rt->prefix) {
+ rt->prefix = dstlen;
+ str_ncpy(rt->ifname, _ifname, rt->size);
+ return false;
+ }
+ break;
+#endif
+ }
+
+ return false;
+}
+
+
+/**
+ * Get the interface name of the default route
+ *
+ * @param af Address family
+ * @param ifname Buffer for returned interface name
+ * @param size Size of buffer
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_rt_default_get(int af, char *ifname, size_t size)
+{
+ struct net_rt rt;
+ int err;
+
+ rt.af = af;
+ rt.ifname = ifname;
+ rt.size = size;
+ rt.prefix = 256;
+
+ err = net_rt_list(rt_default_get_handler, &rt);
+ if (err)
+ return err;
+
+ return '\0' != ifname[0] ? 0 : EINVAL;
+}
+
+
+#ifndef HAVE_ROUTE_LIST
+/* We must provide a stub */
+int net_rt_list(net_rt_h *rth, void *arg)
+{
+ (void)rth;
+ (void)arg;
+ return ENOSYS;
+}
+#endif
diff --git a/src/net/sock.c b/src/net/sock.c
new file mode 100644
index 0000000..7b74dee
--- /dev/null
+++ b/src/net/sock.c
@@ -0,0 +1,91 @@
+/**
+ * @file net/sock.c Networking sockets code
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_mbuf.h>
+#include <re_net.h>
+
+
+#define DEBUG_MODULE "netsock"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+static bool inited = false;
+
+
+#ifdef WIN32
+static int wsa_init(void)
+{
+ WORD wVersionRequested = MAKEWORD(2, 2);
+ WSADATA wsaData;
+ int err;
+
+ err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0) {
+ DEBUG_WARNING("Could not load winsock (%m)\n", err);
+ return err;
+ }
+
+ /* Confirm that the WinSock DLL supports 2.2.*/
+ /* Note that if the DLL supports versions greater */
+ /* than 2.2 in addition to 2.2, it will still return */
+ /* 2.2 in wVersion since that is the version we */
+ /* requested. */
+ if (LOBYTE(wsaData.wVersion) != 2 ||
+ HIBYTE(wsaData.wVersion) != 2 ) {
+ WSACleanup();
+ DEBUG_WARNING("Bad winsock verion (%d.%d)\n",
+ HIBYTE(wsaData.wVersion),
+ LOBYTE(wsaData.wVersion));
+ return EINVAL;
+ }
+
+ return 0;
+}
+#endif
+
+
+/**
+ * Initialise network sockets
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_sock_init(void)
+{
+ int err = 0;
+
+ DEBUG_INFO("sock init: inited=%d\n", inited);
+
+ if (inited)
+ return 0;
+
+#ifdef WIN32
+ err = wsa_init();
+#endif
+
+ inited = true;
+
+ return err;
+}
+
+
+/**
+ * Cleanup network sockets
+ */
+void net_sock_close(void)
+{
+#ifdef WIN32
+ const int err = WSACleanup();
+ if (0 != err) {
+ DEBUG_WARNING("sock close: WSACleanup (%d)\n", err);
+ }
+#endif
+
+ inited = false;
+
+ DEBUG_INFO("sock close\n");
+}
diff --git a/src/net/sockopt.c b/src/net/sockopt.c
new file mode 100644
index 0000000..2385a69
--- /dev/null
+++ b/src/net/sockopt.c
@@ -0,0 +1,112 @@
+/**
+ * @file sockopt.c Networking socket options
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_net.h>
+
+
+#define DEBUG_MODULE "sockopt"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/** Platform independent buffer type cast */
+#ifdef WIN32
+#define BUF_CAST (char *)
+#else
+#define BUF_CAST
+#endif
+
+
+/**
+ * Set socket option blocking or non-blocking
+ *
+ * @param fd Socket file descriptor
+ * @param blocking true for blocking, false for non-blocking
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_sockopt_blocking_set(int fd, bool blocking)
+{
+#ifdef WIN32
+ unsigned long noblock = !blocking;
+ int err = 0;
+
+ if (0 != ioctlsocket(fd, FIONBIO, &noblock)) {
+ err = WSAGetLastError();
+ DEBUG_WARNING("nonblock set: fd=%d err=%d (%m)\n",
+ fd, err, err);
+ }
+ return err;
+#else
+ int flags;
+ int err = 0;
+
+ flags = fcntl(fd, F_GETFL);
+ if (-1 == flags) {
+ err = errno;
+ DEBUG_WARNING("sockopt set: fnctl F_GETFL: (%m)\n", err);
+ goto out;
+ }
+
+ if (blocking)
+ flags &= ~O_NONBLOCK;
+ else
+ flags |= O_NONBLOCK;
+
+ if (-1 == fcntl(fd, F_SETFL, flags)) {
+ err = errno;
+ DEBUG_WARNING("sockopt set: fcntl F_SETFL non-block (%m)\n",
+ err);
+ }
+
+ out:
+ return err;
+#endif
+}
+
+
+/**
+ * Set socket option to reuse address and port
+ *
+ * @param fd Socket file descriptor
+ * @param reuse true for reuse, false for no reuse
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_sockopt_reuse_set(int fd, bool reuse)
+{
+ int r = reuse;
+
+#ifdef SO_REUSEADDR
+ if (-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ BUF_CAST &r, sizeof(r))) {
+ DEBUG_WARNING("SO_REUSEADDR: %m\n", errno);
+ return errno;
+ }
+#endif
+
+#ifdef SO_REUSEPORT
+ if (-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEPORT,
+ BUF_CAST &r, sizeof(r))) {
+ DEBUG_INFO("SO_REUSEPORT: %m\n", errno);
+ return errno;
+ }
+#endif
+
+#if !defined(SO_REUSEADDR) && !defined(SO_REUSEPORT)
+ (void)r;
+ (void)fd;
+ (void)reuse;
+ return ENOSYS;
+#else
+ return 0;
+#endif
+}
diff --git a/src/net/win32/wif.c b/src/net/win32/wif.c
new file mode 100644
index 0000000..f24894f
--- /dev/null
+++ b/src/net/win32/wif.c
@@ -0,0 +1,127 @@
+/**
+ * @file wif.c Windows network interface code
+ *
+ * Copyright (C) 2010 Creytiv.com
+ */
+#include <winsock2.h>
+#include <iphlpapi.h>
+#include <re_types.h>
+#include <re_fmt.h>
+#include <re_net.h>
+#include <re_sa.h>
+
+
+#define DEBUG_MODULE "wif"
+#define DEBUG_LEVEL 5
+#include <re_dbg.h>
+
+
+/**
+ * List interfaces using GetAdaptersAddresses, which handles both
+ * IPv4 and IPv6 address families.
+ *
+ * This is available from Windows XP and Windows Server 2003
+ */
+static int if_list_gaa(net_ifaddr_h *ifh, void *arg)
+{
+ IP_ADAPTER_ADDRESSES addrv[64], *cur;
+ ULONG ret, len = sizeof(addrv);
+ const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST;
+ HANDLE hLib;
+ union {
+ FARPROC proc;
+ ULONG (WINAPI *gaa)(ULONG, ULONG, PVOID,
+ PIP_ADAPTER_ADDRESSES, PULONG);
+ } u;
+ bool stop = false;
+ int err = 0;
+
+ hLib = LoadLibrary(TEXT("iphlpapi.dll"));
+ if (!hLib)
+ return ENOSYS;
+
+ u.proc = GetProcAddress(hLib, TEXT("GetAdaptersAddresses"));
+ if (!u.proc) {
+ err = ENOSYS;
+ goto out;
+ }
+
+ ret = (*u.gaa)(AF_UNSPEC, flags, NULL, addrv, &len);
+ if (ret != ERROR_SUCCESS) {
+ DEBUG_WARNING("if_list: GetAdaptersAddresses ret=%u\n", ret);
+ err = ENODEV;
+ goto out;
+ }
+
+ for (cur = addrv; cur && !stop; cur = cur->Next) {
+ PIP_ADAPTER_UNICAST_ADDRESS ip;
+
+ /* an interface can have many IP-addresses */
+ for (ip = cur->FirstUnicastAddress; ip; ip = ip->Next) {
+ struct sa sa;
+
+ sa_set_sa(&sa, ip->Address.lpSockaddr);
+
+ if (ifh && ifh(cur->AdapterName, &sa, arg)) {
+ stop = true;
+ break;
+ }
+ }
+ }
+
+ out:
+ FreeLibrary(hLib);
+
+ return err;
+}
+
+
+/**
+ * List interfaces using GetAdaptersInfo, which handles only IPv4 family.
+ *
+ * This is available from Windows 2000, and also works under Wine.
+ */
+static int if_list_gai(net_ifaddr_h *ifh, void *arg)
+{
+ IP_ADAPTER_INFO info[32];
+ PIP_ADAPTER_INFO p = info;
+ ULONG ulOutBufLen = sizeof(info);
+ DWORD ret;
+
+ ret = GetAdaptersInfo(info, &ulOutBufLen);
+ if (ret != ERROR_SUCCESS) {
+ DEBUG_WARNING("if_list: GetAdaptersInfo ret=%u\n", ret);
+ return ENODEV;
+ }
+
+ for (p = info; p; p = p->Next) {
+ struct sa sa;
+
+ if (sa_set_str(&sa, p->IpAddressList.IpAddress.String, 0))
+ continue;
+
+ if (ifh && ifh(p->AdapterName, &sa, arg))
+ break;
+ }
+
+ return 0;
+}
+
+
+/**
+ * Enumerate all network interfaces
+ *
+ * @param ifh Interface handler
+ * @param arg Handler argument
+ *
+ * @return 0 if success, otherwise errorcode
+ */
+int net_if_list(net_ifaddr_h *ifh, void *arg)
+{
+ /* Try both methods .. */
+
+ if (!if_list_gaa(ifh, arg))
+ return 0;
+
+ return if_list_gai(ifh, arg);
+}