+#elif defined __FreeBSD__
+
+#if defined __FreeBSD_USE_KVM
+
+static kvm_t *kvmfd;
+static struct nlist symbols[] =
+{
+ {"_ifnet"},
+ {NULL}
+};
+static u_long ifnet_savedaddr;
+static gid_t if_rgid;
+static gid_t if_egid;
+
+void
+interface_set_gids(gid_t egid, gid_t rgid)
+{
+ if_rgid = rgid;
+ if_egid = egid;
+}
+
+static int
+openkvm(void)
+{
+ if ((kvmfd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
+ return FALSE;
+
+ if (kvm_nlist(kvmfd, symbols) < 0)
+ return FALSE;
+
+ if (kvm_read(kvmfd, (unsigned long) symbols[0].n_value, &ifnet_savedaddr, sizeof(unsigned long)) == -1)
+ return FALSE;
+
+ return TRUE;
+}
+
+static int
+get_ifinfo(const char *ifname, ifinfo_t *ifinfo)
+{
+ char tname[16];
+ char iname[16];
+ struct ifnet ifnet;
+ unsigned long ifnet_addr = ifnet_savedaddr;
+ struct ifnethead ifnethead;
+ struct ifaddrhead ifaddrhead;
+ struct ifaddr ifaddr;
+ unsigned long ifaddr_addr;
+ struct sockaddr sa;
+ uint i;
+
+ if (if_egid)
+ setegid(if_egid);
+
+ for (i = 0; ifname[i] && ifname[i] != '/' && i < sizeof(iname) - 1; i++)
+ iname[i] = ifname[i];
+
+ iname[i] = '\0';
+
+ if (!kvmfd)
+ {
+ if (!openkvm())
+ {
+ report(stderr, 0, GT_("Unable to open kvm interface. Make sure fetchmail is SGID kmem."));
+ if (if_egid)
+ setegid(if_rgid);
+ exit(1);
+ }
+ }
+
+ kvm_read(kvmfd, ifnet_savedaddr, (char *) &ifnethead, sizeof ifnethead);
+ ifnet_addr = (u_long) ifnethead.tqh_first;
+
+ while (ifnet_addr)
+ {
+ kvm_read(kvmfd, ifnet_addr, &ifnet, sizeof(ifnet));
+ kvm_read(kvmfd, (unsigned long) ifnet.if_name, tname, sizeof tname);
+ snprintf(tname + strlen(tname), sizeof(tname) - strlen(tname), "%d", ifnet.if_unit);
+
+ if (!strcmp(tname, iname))
+ {
+ if (!(ifnet.if_flags & IFF_UP))
+ {
+ if (if_egid)
+ setegid(if_rgid);
+ return 0;
+ }
+
+ ifinfo->rx_packets = ifnet.if_ipackets;
+ ifinfo->tx_packets = ifnet.if_opackets;
+
+ ifaddr_addr = (u_long) ifnet.if_addrhead.tqh_first;
+
+ while(ifaddr_addr)
+ {
+ kvm_read(kvmfd, ifaddr_addr, &ifaddr, sizeof(ifaddr));
+ kvm_read(kvmfd, (u_long)ifaddr.ifa_addr, &sa, sizeof(sa));
+
+ if (sa.sa_family != AF_INET)
+ {
+ ifaddr_addr = (u_long) ifaddr.ifa_link.tqe_next;
+ continue;
+ }
+
+ ifinfo->addr.s_addr = *(u_long *)(sa.sa_data + 2);
+ kvm_read(kvmfd, (u_long)ifaddr.ifa_dstaddr, &sa, sizeof(sa));
+ ifinfo->dstaddr.s_addr = *(u_long *)(sa.sa_data + 2);
+ kvm_read(kvmfd, (u_long)ifaddr.ifa_netmask, &sa, sizeof(sa));
+ ifinfo->netmask.s_addr = *(u_long *)(sa.sa_data + 2);
+
+ if (if_egid)
+ setegid(if_rgid);
+
+ return 1;
+ }
+
+ if (if_egid)
+ setegid(if_rgid);
+
+ return 0;
+ }
+
+ ifnet_addr = (u_long) ifnet.if_link.tqe_next;
+ }
+
+ if (if_egid)
+ setegid(if_rgid);
+
+ return 0;
+}
+
+#else /* Do not use KVM on FreeBSD */
+
+/*
+ * Expand the compacted form of addresses as returned via the
+ * configuration read via sysctl().
+ */
+
+static void
+rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo *rtinfo)
+{
+ struct sockaddr *sa;
+ int i;
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+ memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
+ for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ if ((rtinfo->rti_addrs & (1 << i)) == 0)
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ ADVANCE(cp, sa);
+ }
+
+#undef ROUNDUP
+#undef ADVANCE
+}
+
+static int
+get_ifinfo(const char *ifname, ifinfo_t *ifinfo)
+{
+ uint i;
+ int rc = 0;
+ int ifindex = -1;
+ size_t needed;
+ char *buf = NULL;
+ char *lim = NULL;
+ char *next = NULL;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_in *sin;
+ struct sockaddr_dl *sdl;
+ struct rt_addrinfo info;
+ char iname[16];
+ int mib[6];
+
+ memset(ifinfo, 0, sizeof(*ifinfo));
+
+ /* trim interface name */
+
+ for (i = 0; i < sizeof(iname) && ifname[i] && ifname[i] != '/'; i++)
+ iname[i] = ifname[i];
+
+ if (i == 0 || i == sizeof(iname))
+ {
+ report(stderr, GT_("Unable to parse interface name from %s"), ifname);
+ return 0;
+ }
+
+ iname[i] = 0;
+
+
+ /* get list of existing interfaces */
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET; /* Only IP addresses please. */
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0; /* List all interfaces. */
+
+
+ /* Get interface data. */
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) == -1)
+ {
+ report(stderr,
+ GT_("get_ifinfo: sysctl (iflist estimate) failed"));
+ exit(1);
+ }
+ if ((buf = (char *)malloc(needed)) == NULL)
+ {
+ report(stderr,
+ GT_("get_ifinfo: malloc failed"));
+ exit(1);
+ }
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) == -1)
+ {
+ report(stderr,
+ GT_("get_ifinfo: sysctl (iflist) failed"));
+ exit(1);
+ }
+
+ lim = buf+needed;
+
+
+ /* first look for the interface information */
+
+ next = buf;
+ while (next < lim)
+ {
+ ifm = (struct if_msghdr *)next;
+ next += ifm->ifm_msglen;
+
+ if (ifm->ifm_version != RTM_VERSION)
+ {
+ report(stderr,
+ GT_("Routing message version %d not understood."),
+ ifm->ifm_version);
+ exit(1);
+ }
+
+ if (ifm->ifm_type == RTM_IFINFO)
+ {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+
+ if (!(strlen(iname) == sdl->sdl_nlen
+ && strncmp(iname, sdl->sdl_data, sdl->sdl_nlen) == 0))
+ {
+ continue;
+ }
+
+ if ( !(ifm->ifm_flags & IFF_UP) )
+ {
+ /* the interface is down */
+ goto get_ifinfo_end;
+ }
+
+ ifindex = ifm->ifm_index;
+ ifinfo->rx_packets = ifm->ifm_data.ifi_ipackets;
+ ifinfo->tx_packets = ifm->ifm_data.ifi_opackets;
+
+ break;
+ }
+ }
+
+ if (ifindex < 0)
+ {
+ /* we did not find an interface with a matching name */
+ report(stderr, GT_("No interface found with name %s"), iname);
+ goto get_ifinfo_end;
+ }
+
+ /* now look for the interface's IP address */
+
+ next = buf;
+ while (next < lim)
+ {
+ ifam = (struct ifa_msghdr *)next;
+ next += ifam->ifam_msglen;
+
+ if (ifindex > 0
+ && ifam->ifam_type == RTM_NEWADDR
+ && ifam->ifam_index == ifindex)
+ {
+ /* Expand the compacted addresses */
+ info.rti_addrs = ifam->ifam_addrs;
+ rt_xaddrs((char *)(ifam + 1),
+ ifam->ifam_msglen + (char *)ifam,
+ &info);
+
+ /* Check for IPv4 address information only */
+ if (info.rti_info[RTAX_IFA]->sa_family != AF_INET)
+ {
+ continue;
+ }
+
+ rc = 1;
+
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_IFA];
+ if (sin)
+ {
+ ifinfo->addr = sin->sin_addr;
+ }
+
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_NETMASK];
+ if (sin)
+ {
+ ifinfo->netmask = sin->sin_addr;
+ }
+
+ /* note: RTAX_BRD contains the address at the other
+ * end of a point-to-point link or the broadcast address
+ * of non point-to-point link
+ */
+ sin = (struct sockaddr_in *)info.rti_info[RTAX_BRD];
+ if (sin)
+ {
+ ifinfo->dstaddr = sin->sin_addr;
+ }
+
+ break;
+ }
+ }
+
+ if (rc == 0)
+ {
+ report(stderr, GT_("No IP address found for %s"), iname);
+ }
+
+get_ifinfo_end:
+ free(buf);
+ return rc;
+}
+
+#endif /* __FREEBSD_USE_SYSCTL_GET_IFFINFO */
+
+#endif
+
+#ifndef HAVE_INET_ATON
+/*
+ * Note: This is not a true replacement for inet_aton(), as it won't
+ * do the right thing on "255.255.255.255" (which translates to -1 on
+ * most machines). Fortunately this code will be used only if you're
+ * on an older Linux that lacks a real implementation.
+ */
+#ifdef HAVE_NETINET_IN_SYSTM_H
+# include <sys/types.h>
+# include <netinet/in_systm.h>
+#endif
+
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <string.h>
+
+static int inet_aton(const char *cp, struct in_addr *inp) {
+ long addr;
+
+ addr = inet_addr(cp);
+ if (addr == ((long) -1)) return 0;
+
+ memcpy(inp, &addr, sizeof(addr));
+ return 1;
+}
+#endif /* HAVE_INET_ATON */
+