/******************************************************************************/ /* */ /* 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; }