Show misc_network.c syntax highlighted
/*
*
* Copyright (c) 2003 The Regents of the University of California. All
* rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Neither the name of the University nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS 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 REGENTS 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.
*
*/
/*
* network utilities
*
* part of libmisc
*
* $Id: misc_network.c,v 1.12 2004/12/07 00:02:40 jelson Exp $
*/
char misc_network_c_cvsid[] = "$Id: misc_network.c,v 1.12 2004/12/07 00:02:40 jelson Exp $";
#include "misc.h"
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <arpa/inet.h>
#include <netdb.h>
/* perform host lookup and return address, in network order */
int misc_lookup_host(char *address, struct in_addr *addr)
{
struct hostent *host;
host = gethostbyname(address);
if (host && host->h_addr_list[0]) {
memmove(addr, host->h_addr_list[0], sizeof(struct in_addr));
return 0;
}
return -1;
}
/* attempt to get the fqdn, return a copy to be freed by the user */
char *misc_get_fqdn(char* servername) {
const char *fqdn = NULL, *search = NULL;
int count = 0;
int fqdndotcount = 0;
struct hostent *host = gethostbyname(servername);
if (host == NULL)
return strdup(servername);
/**
* This is pretty stupid. But its a start. We find the fqdn by
* counting the number of dots. The more dots, the better. How would
* this work with IP addresses?
*/
fqdn = host->h_name;
/* how many dots in the standard fqdn? */
search = fqdn;
while (*search != '\0') {
if (*search == '.')
fqdndotcount++;
search++;
}
/* now go through the rest and find the fqdn */
while (host->h_aliases[count] != NULL) {
int dotcount = 0;
search = host->h_aliases[count];
/* count the dots */
while (*search != '\0') {
if (*search == '.')
dotcount++;
search++;
}
if (dotcount > fqdndotcount) {
fqdndotcount = dotcount;
fqdn = host->h_aliases[count];
}
count++;
}
return strdup(fqdn);
}
/* printable output of a network address */
char *net_unparse_address(struct sockaddr_in *addr)
{
DECLARE_STATIC_BUF_RING(str, 5, 80);
sprintf(str, "%s:%d", inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
return str;
}
/*
* listen for UDP datagrams destined for the given port AND only the
* given network interface. Returns a file descriptor on success, or
* -1 on failure.
*/
int udp_listen_if(int port, struct in_addr *bind_addr)
{
int s, arg;
struct sockaddr_in sa = {
sin_family: AF_INET,
sin_port: htons(port),
sin_addr: *bind_addr
};
if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
elog(LOG_ERR, "can't create socket: %m");
return -1;
}
if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
elog(LOG_ERR, "couldn't bind UDP socket to port %d: %m", port);
goto err;
}
/* enable broadcast on the socket, in case the same FD is also used
* for outgoing packets (it often is) */
arg = 1;
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *) &arg, sizeof(arg)) < 0) {
elog(LOG_ERR, "can't enable UDP broadcast: %m");
goto err;
}
#ifdef SO_TIMESTAMP
/* tell the socket to give us timestamps */
if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, (char *) &arg, sizeof(arg)) < 0) {
elog(LOG_ERR, "can't get timestamps from socket: %m");
goto err;
}
#endif
return s;
err:
close(s);
return -1;
}
/*
* listen for UDP datagrams destined for the given port, from any
* network interface on the machine.
*/
int udp_listen(int port)
{
struct in_addr bind_addr = {
s_addr: INADDR_ANY
};
return udp_listen_if(port, &bind_addr);
}
#ifdef SO_TIMESTAMP
/* For systems that have the SO_TIMESTAMP socket option, this function
* reads a UDP datagram from a socket, filling in the length of the
* packet read (in "len", which is a value-return parameter), the
* address from which the packet was received (in "addr"), andthe
* kernel-acquired time at which it was received (in "tv").
*
* In case of error, retval is -1 and len is set to 0.
*/
int udp_read_with_timestamp(int fd, char *buf, int *len,
struct sockaddr_in *addr, struct timeval *tv)
{
char ans_data[4096];
int retval;
struct cmsghdr *c;
struct iovec iov = {
iov_base: buf,
iov_len: *len,
};
struct msghdr m = {
msg_name: addr,
msg_namelen: sizeof(struct sockaddr_in),
msg_iov: &iov,
msg_iovlen: 1,
msg_control: ans_data,
msg_controllen: sizeof(ans_data),
};
/* read both the packet and the extra data off the wire */
if ((retval = recvmsg(fd, &m, 0)) < 0) {
*len = 0;
return retval;
}
/* retval is the length read - return it */
*len = retval;
/* now see if there's a timestamp */
for (c = CMSG_FIRSTHDR(&m); c; c = CMSG_NXTHDR(&m, c)) {
if (c->cmsg_level != SOL_SOCKET || c->cmsg_type != SO_TIMESTAMP)
continue;
if (c->cmsg_len != CMSG_LEN(sizeof(struct timeval)))
continue;
memcpy(tv, CMSG_DATA(c), sizeof(struct timeval));
return retval;
}
/* no timestamp!! fall back to gettimeofday and complain */
elog(LOG_WARNING, "SO_TIMESTAMP failed!!");
gettimeofday(tv, NULL);
return retval;
}
#else
/*
* If the system deos not have SO_TIMESTAMP, we have a simplified
* version of the function that just reads the packet using plain old
* 'recvfrom'. The timestamp is filled in manually using
* gettimeofday; i.e., this is not a kernel-acquired timestamp.
*/
int udp_read_with_timestamp(int fd, char *buf, int *len,
struct sockaddr_in *addr, struct timeval *tv)
{
int retval;
int socklen = sizeof(*addr);
gettimeofday(tv, NULL);
retval = recvfrom(fd, buf, *len, 0, (struct sockaddr *) addr, &socklen);
if (retval < 0)
*len = 0;
else
*len = retval;
return retval;
}
#endif /* SO_TIMESTAMP */
/*
* given the name of a network interface, find the IP address and
* broadcast address associated with it. If successful, returns 0 and
* writes address to 'addr'. On failure returns -1.
*/
int get_ifaddr_by_name(char *name, struct in_addr *addr,
struct in_addr *bcast)
{
struct ifreq ifreq;
struct sockaddr_in sin;
int s, retval = -1;
if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
elog(LOG_ERR, "can't create socket: %m");
return -1;
}
/* Get the interface address */
memset(&ifreq, 0, sizeof(ifreq));
strcpy(ifreq.ifr_name, name);
if (ioctl(s, SIOCGIFADDR, &ifreq) < 0) {
elog(LOG_ERR, "can't get interface address of %s: %m", name);
goto done;
}
if (ifreq.ifr_addr.sa_family != AF_INET) {
elog(LOG_ERR, "%s's address not an IP address!", name);
goto done;
}
memset(&sin, 0, sizeof(sin));
memcpy(&sin, &ifreq.ifr_addr, sizeof(struct sockaddr));
if (addr) *addr = sin.sin_addr;
/* Now get the broadcast address */
memset(&ifreq, 0, sizeof(ifreq));
strcpy(ifreq.ifr_name, name);
if (ioctl(s, SIOCGIFBRDADDR, &ifreq) < 0) {
elog(LOG_ERR, "can't get broadcast address of %s: %m", name);
goto done;
}
if (ifreq.ifr_broadaddr.sa_family != AF_INET) {
elog(LOG_ERR, "%s's broadcast addr not an IP address!", name);
goto done;
}
memset(&sin, 0, sizeof(sin));
memcpy(&sin, &ifreq.ifr_broadaddr, sizeof(struct sockaddr));
if (bcast) *bcast = sin.sin_addr;
/* success! */
retval = 0;
done:
close(s);
return retval;
}
See more files for this project here