/*
* recvfromto Like recvfrom, but also stores the destination
* IP address. Useful on multihomed hosts.
*
* Should work on Linux and BSD.
*
* Copyright (C) 2002 Miquel van Smoorenburg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
/* Remove this when autoconf can detect this. */
#if defined(IP_PKTINFO) && !defined(HAVE_IP_PKTINFO)
# define HAVE_IP_PKTINFO 1
#elif defined(IP_RECVDSTADDR) && !defined(HAVE_IP_RECVDSTADDR)
# define HAVE_IP_RECVDSTADDR
#endif
int recvfromto(int s, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen,
struct sockaddr *to, socklen_t *tolen)
{
struct msghdr msgh;
struct cmsghdr *cmsg;
struct iovec iov;
char cbuf[1024];
int opt, err;
/*
* If from or to are set, they must be big enough
* to store a struct sockaddr_in.
*/
if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) ||
(to && (!tolen || *tolen < sizeof(struct sockaddr_in)))) {
errno = EINVAL;
return -1;
}
if (tolen) *tolen = 0;
#ifdef HAVE_IP_PKTINFO
/*
* IP_PKTINFO doesn't provide sin_port so we have to
* retrieve it using getsockname().
*/
if (to) {
struct sockaddr_in si;
socklen_t l = sizeof(si);
((struct sockaddr_in *)to)->sin_family = AF_INET;
((struct sockaddr_in *)to)->sin_port = 0;
l = sizeof(si);
if (getsockname(s, (struct sockaddr *)&si, &l) == 0) {
((struct sockaddr_in *)to)->sin_port = si.sin_port;
((struct sockaddr_in *)to)->sin_addr = si.sin_addr;
}
*tolen = sizeof(struct sockaddr_in);
}
#endif
/* Set MSG_DONTWAIT if O_NONBLOCK was set. */
if (fcntl(s, F_GETFL) & O_NONBLOCK) flags |= MSG_DONTWAIT;
/* Set up iov and msgh structures. */
iov.iov_base = buf;
iov.iov_len = len;
msgh.msg_control = cbuf;
msgh.msg_controllen = sizeof(cbuf);
msgh.msg_name = from;
msgh.msg_namelen = fromlen ? *fromlen : 0;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
#ifdef HAVE_IP_PKTINFO
/* Set the IP_PKTINFO option (Linux). */
opt = 1;
setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
#endif
#ifdef HAVE_IP_RECVDSTADDR
/* Set the IP_RECVDSTADDR option (BSD). */
opt = 1;
setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
#endif
/* Receive one packet. */
err = recvmsg(s, &msgh, flags);
if (fromlen) *fromlen = msgh.msg_namelen;
/* Process auxiliary received data in msgh */
for (cmsg = CMSG_FIRSTHDR(&msgh);
cmsg != NULL && cmsg->cmsg_len >= sizeof(*cmsg);
cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
#ifdef HAVE_IP_PKTINFO
if (cmsg->cmsg_level == SOL_IP
&& cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *i =
(struct in_pktinfo *)CMSG_DATA(cmsg);
if (to) {
((struct sockaddr_in *)to)->sin_addr =
i->ipi_addr;
*tolen = sizeof(struct sockaddr_in);
}
break;
}
#endif
#ifdef HAVE_IP_RECVDSTADDR
if (cmsg->cmsg_level == IPPROTO_IP
&& cmsg->cmsg_type == IP_RECVDSTADDR) {
struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
if (to) {
((struct sockaddr_in *)to)->sin_addr = *i;
*tolen = sizeof(struct sockaddr_in);
}
}
#endif
}
return err;
}
#ifdef STANDALONE
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
/*
* Small test program to test recvfromto
*/
int main(int argc, char **argv)
{
struct sockaddr_in from, to, in;
char buf[1024];
int port = 20000;
int n, s, fl, tl;
if (argc > 1) port = atoi(argv[1]);
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
in.sin_family = AF_INET;
in.sin_port = htons(port);
in.sin_addr.s_addr = INADDR_ANY;
bind(s, &in, sizeof(in));
while (1) {
fl = tl = sizeof(struct sockaddr_in);
memset(&from, 0, sizeof(from));
memset(&to, 0, sizeof(to));
if ((n = recvfromto(s, buf, sizeof(buf), 0,
(struct sockaddr *)&from, &fl,
(struct sockaddr *)&to, &tl)) < 0) {
perror("recvfromto");
break;
}
printf("Received a packet of %d bytes/n", n);
printf(" src ip:port %s:%d/n",
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
printf(" dst ip:port %s:%d/n",
inet_ntoa(to.sin_addr), ntohs(to.sin_port));
}
return 0;
}
#endif /* STANDALONE */
* recvfromto Like recvfrom, but also stores the destination
* IP address. Useful on multihomed hosts.
*
* Should work on Linux and BSD.
*
* Copyright (C) 2002 Miquel van Smoorenburg.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <netinet/in.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
/* Remove this when autoconf can detect this. */
#if defined(IP_PKTINFO) && !defined(HAVE_IP_PKTINFO)
# define HAVE_IP_PKTINFO 1
#elif defined(IP_RECVDSTADDR) && !defined(HAVE_IP_RECVDSTADDR)
# define HAVE_IP_RECVDSTADDR
#endif
int recvfromto(int s, void *buf, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen,
struct sockaddr *to, socklen_t *tolen)
{
struct msghdr msgh;
struct cmsghdr *cmsg;
struct iovec iov;
char cbuf[1024];
int opt, err;
/*
* If from or to are set, they must be big enough
* to store a struct sockaddr_in.
*/
if ((from && (!fromlen || *fromlen < sizeof(struct sockaddr_in))) ||
(to && (!tolen || *tolen < sizeof(struct sockaddr_in)))) {
errno = EINVAL;
return -1;
}
if (tolen) *tolen = 0;
#ifdef HAVE_IP_PKTINFO
/*
* IP_PKTINFO doesn't provide sin_port so we have to
* retrieve it using getsockname().
*/
if (to) {
struct sockaddr_in si;
socklen_t l = sizeof(si);
((struct sockaddr_in *)to)->sin_family = AF_INET;
((struct sockaddr_in *)to)->sin_port = 0;
l = sizeof(si);
if (getsockname(s, (struct sockaddr *)&si, &l) == 0) {
((struct sockaddr_in *)to)->sin_port = si.sin_port;
((struct sockaddr_in *)to)->sin_addr = si.sin_addr;
}
*tolen = sizeof(struct sockaddr_in);
}
#endif
/* Set MSG_DONTWAIT if O_NONBLOCK was set. */
if (fcntl(s, F_GETFL) & O_NONBLOCK) flags |= MSG_DONTWAIT;
/* Set up iov and msgh structures. */
iov.iov_base = buf;
iov.iov_len = len;
msgh.msg_control = cbuf;
msgh.msg_controllen = sizeof(cbuf);
msgh.msg_name = from;
msgh.msg_namelen = fromlen ? *fromlen : 0;
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
#ifdef HAVE_IP_PKTINFO
/* Set the IP_PKTINFO option (Linux). */
opt = 1;
setsockopt(s, SOL_IP, IP_PKTINFO, &opt, sizeof(opt));
#endif
#ifdef HAVE_IP_RECVDSTADDR
/* Set the IP_RECVDSTADDR option (BSD). */
opt = 1;
setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt));
#endif
/* Receive one packet. */
err = recvmsg(s, &msgh, flags);
if (fromlen) *fromlen = msgh.msg_namelen;
/* Process auxiliary received data in msgh */
for (cmsg = CMSG_FIRSTHDR(&msgh);
cmsg != NULL && cmsg->cmsg_len >= sizeof(*cmsg);
cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
#ifdef HAVE_IP_PKTINFO
if (cmsg->cmsg_level == SOL_IP
&& cmsg->cmsg_type == IP_PKTINFO) {
struct in_pktinfo *i =
(struct in_pktinfo *)CMSG_DATA(cmsg);
if (to) {
((struct sockaddr_in *)to)->sin_addr =
i->ipi_addr;
*tolen = sizeof(struct sockaddr_in);
}
break;
}
#endif
#ifdef HAVE_IP_RECVDSTADDR
if (cmsg->cmsg_level == IPPROTO_IP
&& cmsg->cmsg_type == IP_RECVDSTADDR) {
struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
if (to) {
((struct sockaddr_in *)to)->sin_addr = *i;
*tolen = sizeof(struct sockaddr_in);
}
}
#endif
}
return err;
}
#ifdef STANDALONE
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
/*
* Small test program to test recvfromto
*/
int main(int argc, char **argv)
{
struct sockaddr_in from, to, in;
char buf[1024];
int port = 20000;
int n, s, fl, tl;
if (argc > 1) port = atoi(argv[1]);
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
in.sin_family = AF_INET;
in.sin_port = htons(port);
in.sin_addr.s_addr = INADDR_ANY;
bind(s, &in, sizeof(in));
while (1) {
fl = tl = sizeof(struct sockaddr_in);
memset(&from, 0, sizeof(from));
memset(&to, 0, sizeof(to));
if ((n = recvfromto(s, buf, sizeof(buf), 0,
(struct sockaddr *)&from, &fl,
(struct sockaddr *)&to, &tl)) < 0) {
perror("recvfromto");
break;
}
printf("Received a packet of %d bytes/n", n);
printf(" src ip:port %s:%d/n",
inet_ntoa(from.sin_addr), ntohs(from.sin_port));
printf(" dst ip:port %s:%d/n",
inet_ntoa(to.sin_addr), ntohs(to.sin_port));
}
return 0;
}
#endif /* STANDALONE */