/******************************************************************************/
/* */
/* X r d N e t . c c */
/* */
/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
/* Produced by Andrew Hanushevsky for Stanford University under contract */
/* DE-AC02-76-SFO0515 with the Department of Energy */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD 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 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
#include
#include
#include
#include
#ifndef WIN32
#include
#include
#include
#include
#else
#include "XrdSys/XrdWin32.hh"
#endif
#include "XrdNet/XrdNet.hh"
#include "XrdNet/XrdNetAddr.hh"
#include "XrdNet/XrdNetBuffer.hh"
#include "XrdNet/XrdNetOpts.hh"
#include "XrdNet/XrdNetPeer.hh"
#include "XrdNet/XrdNetSecurity.hh"
#include "XrdNet/XrdNetSocket.hh"
#include "XrdNet/XrdNetUtils.hh"
#include "XrdSys/XrdSysPlatform.hh"
#include "XrdSys/XrdSysError.hh"
#include "XrdSys/XrdSysFD.hh"
/******************************************************************************/
/* C o n s t r u c t o r */
/******************************************************************************/
XrdNet::XrdNet(XrdSysError *erp, XrdNetSecurity *secp)
{
iofd = PortType = -1;
eDest = erp;
Police = secp;
Domlen = Portnum = Windowsz = netOpts = 0;
Domain = 0;
BuffQ = 0;
}
/******************************************************************************/
/* D e s t r u c t o r */
/******************************************************************************/
XrdNet::~XrdNet()
{
unBind();
if (Domain) free(Domain);
}
/******************************************************************************/
/* A c c e p t */
/******************************************************************************/
int XrdNet::Accept(XrdNetAddr &myAddr, int opts, int timeout)
{
int retc;
// Make sure we are bound to a port
//
opts |= netOpts;
if (iofd < 0)
{if (!(opts & XRDNET_NOEMSG))
eDest->Emsg("Accept", "Network not bound to a port.");
return 0;
}
// This interface only accepts TCP connections
//
if (PortType != SOCK_STREAM)
{if (!(opts & XRDNET_NOEMSG))
eDest->Emsg("Accept", "UDP network not supported for NetAddr call.");
return 0;
}
// Setup up the poll structure to wait for new connections
//
do {if (timeout >= 0)
{struct pollfd sfd = {iofd,
POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI|POLLHUP,0};
do {retc = poll(&sfd, 1, timeout*1000);}
while(retc < 0 && (errno == EAGAIN || errno == EINTR));
if (!retc)
{if (!(opts & XRDNET_NOEMSG))
eDest->Emsg("Accept", "Accept timed out.");
return 0;
}
}
} while(!do_Accept_TCP(myAddr, opts));
return 1;
}
/******************************************************************************/
int XrdNet::Accept(XrdNetPeer &myPeer, int opts, int timeout)
{
int retc;
// Make sure we are bound to a port
//
opts |= netOpts;
if (iofd < 0)
{if (!(opts & XRDNET_NOEMSG))
eDest->Emsg("Accept", "Network not bound to a port.");
return 0;
}
// Setup up the poll structure to wait for new connections
//
do {if (timeout >= 0)
{struct pollfd sfd = {iofd,
POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI|POLLHUP,0};
do {retc = poll(&sfd, 1, timeout*1000);}
while(retc < 0 && (errno == EAGAIN || errno == EINTR));
if (!retc)
{if (!(opts & XRDNET_NOEMSG))
eDest->Emsg("Accept", "Accept timed out.");
return 0;
}
}
} while(!(PortType == SOCK_STREAM ? do_Accept_TCP(myPeer, opts)
: do_Accept_UDP(myPeer, opts)));
// Accept completed, trim the host name if a domain has been specified,
//
if (Domain && !(opts & XRDNET_NODNTRIM)) Trim(myPeer.InetName);
return 1;
}
/******************************************************************************/
/* B i n d */
/******************************************************************************/
int XrdNet::Bind(int bindport, const char *contype)
{
XrdNetSocket mySocket(eDest);
int opts = XRDNET_SERVER | netOpts;
int buffsz = Windowsz;
// Close any open socket here
//
unBind();
// Get correct option settings
//
if (*contype != 'u') PortType = SOCK_STREAM;
else {PortType = SOCK_DGRAM;
opts |= XRDNET_UDPSOCKET;
if (!buffsz) buffsz = XRDNET_UDPBUFFSZ;
}
// Try to open and bind to this port
//
if (mySocket.Open(0, bindport, opts, buffsz) < 0)
return -mySocket.LastError();
// Success, get the socket number and return
//
iofd = mySocket.Detach();
// Obtain port number of generic port being used
//
Portnum = (bindport ? bindport : XrdNetUtils::Port(iofd));
// For udp sockets, we must allocate a buffer queue object
//
if (PortType == SOCK_DGRAM)
{BuffSize = buffsz;
BuffQ = new XrdNetBufferQ(buffsz);
}
return 0;
}
/******************************************************************************/
int XrdNet::Bind(char *path, const char *contype)
{
XrdNetSocket mySocket(eDest);
int opts = XRDNET_SERVER | netOpts;
int buffsz = Windowsz;
// Make sure this is a path and not a host name
//
if (*path != '/')
{eDest->Emsg("Bind", "Invalid bind path -", path);
return -EINVAL;
}
// Close any open socket here
//
unBind();
// Get correct option settings
//
if (*contype != 'd') PortType = SOCK_STREAM;
else {PortType = SOCK_DGRAM;
opts |= XRDNET_UDPSOCKET;
if (!buffsz) buffsz = XRDNET_UDPBUFFSZ;
}
// Try to open and bind to this path
//
if (mySocket.Open(path, -1, opts, buffsz) < 0) return -mySocket.LastError();
// Success, get the socket number and return
//
iofd = mySocket.Detach();
// For udp sockets, we must allocate a buffer queue object
//
if (PortType == SOCK_DGRAM)
{BuffSize = buffsz;
BuffQ = new XrdNetBufferQ(buffsz);
}
return 0;
}
/******************************************************************************/
/* C o n n e c t */
/******************************************************************************/
int XrdNet::Connect(XrdNetAddr &myAddr, const char *host,
int port, int opts, int tmo)
{
XrdNetSocket mySocket(opts & XRDNET_NOEMSG ? 0 : eDest);
// Determine appropriate options but turn off UDP sockets
//
opts = (opts | netOpts) & ~XRDNET_UDPSOCKET;
if (tmo > 0) opts = (opts & ~XRDNET_TOUT) | (tmo > 255 ? 255 : tmo);
// Now perform the connect and return the results if successful
//
if (mySocket.Open(host, port, opts, Windowsz) < 0) return 0;
myAddr.Set(mySocket.Detach());
if (!(opts & XRDNET_NORLKUP)) myAddr.Name();
return 1;
}
/******************************************************************************/
int XrdNet::Connect(XrdNetPeer &myPeer,
const char *host, int port, int opts, int tmo)
{
XrdNetSocket mySocket(opts & XRDNET_NOEMSG ? 0 : eDest);
const struct sockaddr *sap;
int buffsz = Windowsz;
// Determine appropriate options
//
opts |= netOpts;
if ((opts & XRDNET_UDPSOCKET) && !buffsz) buffsz = XRDNET_UDPBUFFSZ;
if (tmo > 0) opts = (opts & ~XRDNET_TOUT) | (tmo > 255 ? 255 : tmo);
// Now perform the connect and return the peer structure if successful
//
if (mySocket.Open(host, port, opts, buffsz) < 0) return 0;
if (myPeer.InetName) free(myPeer.InetName);
if ((opts & XRDNET_UDPSOCKET) || !host)
{myPeer.InetName = strdup("n/a");
memset((void *)&myPeer.Inet, 0, sizeof(myPeer.Inet));
} else {
const char *pn = mySocket.Peername(&sap);
if (pn) {memcpy((void *)&myPeer.Inet, sap, sizeof(myPeer.Inet));
myPeer.InetName = strdup(pn);
if (Domain && !(opts & XRDNET_NODNTRIM)) Trim(myPeer.InetName);
} else {
memset((void *)&myPeer.Inet, 0, sizeof(myPeer.Inet));
myPeer.InetName = strdup("unknown");
}
}
myPeer.fd = mySocket.Detach();
return 1;
}
/******************************************************************************/
/* R e l a y */
/******************************************************************************/
int XrdNet::Relay(XrdNetPeer &Peer, const char *dest, int opts)
{
return Connect(Peer, dest, -1, opts | XRDNET_UDPSOCKET);
}
/******************************************************************************/
int XrdNet::Relay(const char *dest)
{
XrdNetPeer myPeer;
return (Connect(myPeer, dest, -1, XRDNET_UDPSOCKET | XRDNET_SENDONLY)
? myPeer.fd : -1);
}
/******************************************************************************/
/* S e c u r e */
/******************************************************************************/
void XrdNet::Secure(XrdNetSecurity *secp)
{
// If we don't have a Police object then use the one supplied. Otherwise
// merge the supplied object into the existing object.
//
if (Police) Police->Merge(secp);
else Police = secp;
}
/******************************************************************************/
/* T r i m */
/******************************************************************************/
void XrdNet::Trim(char *hname)
{
int k = strlen(hname);
char *hnp;
if (Domlen && k > Domlen)
{hnp = hname + (k - Domlen);
if (!strcmp(Domain, hnp)) *hnp = '\0';
}
}
/******************************************************************************/
/* u n B i n d */
/******************************************************************************/
void XrdNet::unBind()
{
if (iofd >= 0) {close(iofd); iofd=-1; Portnum=0;}
if (BuffQ) {delete BuffQ; BuffQ = 0;}
}
/******************************************************************************/
/* W S i z e */
/******************************************************************************/
int XrdNet::WSize()
{
int wsz;
if (iofd >= 0 && !XrdNetSocket::getWindow(iofd, wsz, eDest)) return wsz;
return 0;
}
/******************************************************************************/
/* P r i v a t e M e t h o d s */
/******************************************************************************/
/******************************************************************************/
/* d o _ A c c e p t _ T C P */
/******************************************************************************/
int XrdNet::do_Accept_TCP(XrdNetAddr &hAddr, int opts)
{
static int noAcpt = 0;
XrdNetSockAddr IP;
SOCKLEN_t addrlen = sizeof(IP);
int newfd;
// Remove UDP option if present
//
opts &= ~XRDNET_UDPSOCKET;
// Accept a connection
//
do {newfd = XrdSysFD_Accept(iofd, &IP.Addr, &addrlen);}
while(newfd < 0 && errno == EINTR);
if (newfd < 0)
{if (!(opts & XRDNET_NOEMSG) && (errno != EMFILE || !(0x1ff & noAcpt++)))
eDest->Emsg("Accept", errno, "perform accept");
return 0;
}
// Initialize the address of the new connection
//
const char *eMsg = hAddr.Set(&IP.Addr, newfd);
if (eMsg)
{char buff[256];
snprintf(buff, sizeof(buff), "%d;", newfd);
eDest->Emsg("Accept", "Failed to identify FD", buff, eMsg);
close(newfd);
return 0;
}
// Remove TCP_NODELAY option for unix domain sockets to avoid error message
//
if (hAddr.isIPType(XrdNetAddrInfo::IPuX)) opts |= XRDNET_DELAY;
// Set all required fd options are set
//
XrdNetSocket::setOpts(newfd, opts, (opts & XRDNET_NOEMSG ? 0 : eDest));
// Authorize by ip address or full (slow) hostname format
//
if (Police)
{if (!Police->Authorize(hAddr))
{char ipbuff[512];
hAddr.Format(ipbuff, sizeof(ipbuff),
(opts & XRDNET_NORLKUP ? XrdNetAddr::fmtAuto
: XrdNetAddr::fmtName),
XrdNetAddrInfo::noPort);
eDest->Emsg("Accept",EACCES,"accept TCP connection from",ipbuff);
close(newfd);
return 0;
}
}
// Force resolution of the addres unless reverse lookup not desired
//
if (!(opts & XRDNET_NORLKUP)) hAddr.Name();
// All done
//
return 1;
}
/******************************************************************************/
int XrdNet::do_Accept_TCP(XrdNetPeer &myPeer, int opts)
{
XrdNetAddr tAddr;
char hBuff[512];
// Use the new interface to actually do the accept
//
if (!do_Accept_TCP(tAddr, opts)) return 0;
// Now transfor it back to the old-style interface
//
memcpy(&myPeer.Inet, tAddr.SockAddr(), tAddr.SockSize());
myPeer.fd = tAddr.SockFD();
tAddr.Format(hBuff, sizeof(hBuff), XrdNetAddr::fmtAuto, false);
if (myPeer.InetName) free(myPeer.InetName);
myPeer.InetName = strdup(hBuff);
return 1;
}
/******************************************************************************/
/* d o _ A c c e p t _ U D P */
/******************************************************************************/
int XrdNet::do_Accept_UDP(XrdNetPeer &myPeer, int opts)
{
char hBuff[512];
int dlen;
XrdNetSockAddr IP;
SOCKLEN_t addrlen = sizeof(IP);
XrdNetBuffer *bp;
XrdNetAddr uAddr;
// For UDP connections, get a buffer for the message. To be thread-safe, we
// must actually receive the message to maintain the host-datagram pairing.
//
if (!(bp = BuffQ->Alloc()))
{eDest->Emsg("Accept", ENOMEM, "accept UDP message");
return 0;
}
// Read the message and get the host address
//
do {dlen = recvfrom(iofd,(Sokdata_t)bp->data,BuffSize-1,0,&IP.Addr,&addrlen);
} while(dlen < 0 && errno == EINTR);
if (dlen < 0)
{eDest->Emsg("Receive", errno, "perform UDP recvfrom()");
BuffQ->Recycle(bp);
return 0;
} else bp->data[dlen] = '\0';
// Use the new-style address handling for address functions
//
uAddr.Set(&IP.Addr);
// Authorize this connection. We don't accept messages that set the
// loopback address since this can be trivially spoofed in UDP packets.
//
if (uAddr.isLoopback() || (Police && !Police->Authorize(uAddr)))
{eDest->Emsg("Accept", -EACCES, "accept connection from",
uAddr.Name("*unknown*"));
BuffQ->Recycle(bp);
return 0;
} else uAddr.Format(hBuff, sizeof(hBuff),
(opts & XRDNET_NORLKUP ? XrdNetAddr::fmtAuto
: XrdNetAddr::fmtName),
XrdNetAddrInfo::noPort);
// Get a new FD if so requested. We use our base FD for outgoing messages.
//
if (opts & XRDNET_NEWFD)
{myPeer.fd = XrdSysFD_Dup(iofd);
if (opts & XRDNET_NOCLOSEX) XrdSysFD_Yield(myPeer.fd);
} else myPeer.fd = iofd;
// Fill in the peer structure.
//
memcpy(&myPeer.Inet, &IP, sizeof(myPeer.Inet));
if (myPeer.InetName) free(myPeer.InetName);
myPeer.InetName = strdup(hBuff);
if (myPeer.InetBuff) myPeer.InetBuff->Recycle();
myPeer.InetBuff = bp;
return 1;
}