tftp-hpa-google/tftpd/recvfrom.c

153 lines
3.8 KiB
C

/* $Id$ */
/* ----------------------------------------------------------------------- *
*
* Copyright 2001 H. Peter Anvin - All Rights Reserved
*
* This program is free software available under the same license
* as the "OpenBSD" operating system, distributed at
* http://www.openbsd.org/.
*
* ----------------------------------------------------------------------- */
/*
* recvfrom.c
*
* Emulate recvfrom() using recvmsg(), but try to capture the local address
* since some TFTP clients consider it an error to get the reply from another
* IP address than the request was sent to.
*
*/
#include "config.h" /* Must be included first! */
#include "recvfrom.h"
#include "tftpsubs.h"
#ifdef HAVE_MACHINE_PARAM_H
#include <machine/param.h> /* Needed on some versions of FreeBSD */
#endif
#if defined(HAVE_RECVMSG) && defined(HAVE_MSGHDR_MSG_CONTROL)
#include <sys/uio.h>
#ifdef IP_PKTINFO
# ifndef HAVE_STRUCT_IN_PKTINFO
# ifdef __linux__
/* Assume this version of glibc simply lacks the definition */
struct in_pktinfo {
int ipi_ifindex;
struct in_addr ipi_spec_dst;
struct in_addr ipi_addr;
};
# else
# undef IP_PKTINFO /* No definition, no way to get it */
# endif
# endif
#endif
#ifndef CMSG_LEN
# define CMSG_LEN(size) (sizeof(struct cmsghdr) + (size))
#endif
#ifndef CMSG_SPACE
# define CMSG_SPACE(size) (sizeof(struct cmsghdr) + (size))
#endif
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
struct sockaddr *from, int *fromlen,
struct sockaddr_in *myaddr)
{
struct msghdr msg;
struct iovec iov;
int n;
struct cmsghdr *cmptr;
union {
struct cmsghdr cm;
#ifdef IP_PKTINFO
char control[CMSG_SPACE(sizeof(struct in_addr)) +
CMSG_SPACE(sizeof(struct in_pktinfo))];
#else
char control[CMSG_SPACE(sizeof(struct in_addr))];
#endif
} control_un;
int on = 1;
#ifdef IP_PKTINFO
struct in_pktinfo pktinfo;
#endif
/* Try to enable getting the return address */
#ifdef IP_RECVDSTADDR
setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR, &on, sizeof(on));
#endif
#ifdef IP_PKTINFO
setsockopt(s, IPPROTO_IP, IP_PKTINFO, &on, sizeof(on));
#endif
bzero(&msg, sizeof msg); /* Clear possible system-dependent fields */
msg.msg_control = control_un.control;
msg.msg_controllen = sizeof(control_un.control);
msg.msg_flags = 0;
msg.msg_name = from;
msg.msg_namelen = *fromlen;
iov.iov_base = buf;
iov.iov_len = len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
if ( (n = recvmsg(s, &msg, flags)) < 0 )
return n; /* Error */
*fromlen = msg.msg_namelen;
if ( myaddr ) {
bzero(myaddr, sizeof(struct sockaddr_in));
myaddr->sin_family = AF_INET;
if ( msg.msg_controllen < sizeof(struct cmsghdr) ||
(msg.msg_flags & MSG_CTRUNC) )
return n; /* No information available */
for ( cmptr = CMSG_FIRSTHDR(&msg) ; cmptr != NULL ;
cmptr = CMSG_NXTHDR(&msg, cmptr) ) {
#ifdef IP_RECVSTDADDR
if ( cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_RECVDSTADDR ) {
memcpy(&myaddr->sin_addr, CMSG_DATA(cmptr),
sizeof(struct in_addr));
}
#endif
#ifdef IP_PKTINFO
if ( cmptr->cmsg_level == IPPROTO_IP &&
cmptr->cmsg_type == IP_PKTINFO ) {
memcpy(&pktinfo, CMSG_DATA(cmptr), sizeof(struct in_pktinfo));
memcpy(&myaddr->sin_addr, &pktinfo.ipi_addr, sizeof(struct in_addr));
}
#endif
}
}
return n;
}
#else /* pointless... */
int
myrecvfrom(int s, void *buf, int len, unsigned int flags,
struct sockaddr *from, int *fromlen,
struct sockaddr_in *myaddr)
{
/* There is no way we can get the local address, fudge it */
bzero(myaddr, sizeof(struct sockaddr_in));
myaddr->sin_family = AF_INET;
myaddr->sin_port = htons(IPPORT_TFTP);
bzero(&myaddr->sin_addr, sizeof(myaddr->sin_addr));
return recvfrom(s,buf,len,flags,from,fromlen);
}
#endif