XRootD
XrdNetSocket.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d N e t S o c k e t . c c */
4 /* */
5 /* (C) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* All Rights Reserved */
7 /* Produced by Andrew Hanushevsky for Stanford University under contract */
8 /* DE-AC02-76-SFO0515 with the Deprtment of Energy */
9 /* */
10 /* This file is part of the XRootD software suite. */
11 /* */
12 /* XRootD is free software: you can redistribute it and/or modify it under */
13 /* the terms of the GNU Lesser General Public License as published by the */
14 /* Free Software Foundation, either version 3 of the License, or (at your */
15 /* option) any later version. */
16 /* */
17 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20 /* License for more details. */
21 /* */
22 /* You should have received a copy of the GNU Lesser General Public License */
23 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25 /* */
26 /* The copyright holder's institutional names and contributor's names may not */
27 /* be used to endorse or promote products derived from this software without */
28 /* specific prior written permission of the institution or contributor. */
29 /******************************************************************************/
30 
31 #ifndef WIN32
32 #include <unistd.h>
33 #include <cctype>
34 #include <cerrno>
35 #include <fcntl.h>
36 #include <poll.h>
37 #include <cstdio>
38 #include <cstdlib>
39 #include <strings.h>
40 #include <netinet/in.h>
41 #include <netinet/tcp.h>
42 #include <sys/types.h>
43 #include <sys/socket.h>
44 #include <sys/stat.h>
45 #include <sys/un.h>
46 #else
47 #include <cerrno>
48 #include <fcntl.h>
49 #include <cstdio>
50 #include <cstdlib>
51 #include <sys/types.h>
52 #include <sys/stat.h>
53 #include <Winsock2.h>
54 #include "XrdSys/XrdWin32.hh"
55 #endif
56 
57 #include "XrdNet/XrdNetConnect.hh"
58 #include "XrdNet/XrdNetOpts.hh"
59 #include "XrdNet/XrdNetSocket.hh"
60 #include "XrdNet/XrdNetUtils.hh"
61 #include "XrdOuc/XrdOucUtils.hh"
62 #include "XrdSys/XrdSysError.hh"
63 #include "XrdSys/XrdSysFD.hh"
64 #include "XrdSys/XrdSysPlatform.hh"
65 
66 /******************************************************************************/
67 /* G l o b a l s */
68 /******************************************************************************/
69 
70 namespace XrdNetSocketCFG
71 {
72  int ka_Idle = 0;
73  int ka_Itvl = 0;
74  int ka_Icnt = 0;
75 };
76 
77 /******************************************************************************/
78 /* l o c a l d e f i n e s */
79 /******************************************************************************/
80 
81 #define Err(p,a,b,c) (ErrCode = (eroute ? eroute->Emsg(#p, a, b, c) : ErrCode),-1)
82 #define ErrM(p,a,b,c) (ErrCode = (eroute ? eroute->Emsg(#p, a, b, c) : ErrCode),-1)
83 
84 /******************************************************************************/
85 /* C o n s t r u c t o r */
86 /******************************************************************************/
87 
88 XrdNetSocket::XrdNetSocket(XrdSysError *erobj, int SockFileDesc)
89 {
90  ErrCode = 0;
91  eroute = erobj;
92  SockFD = SockFileDesc;
93 }
94 
95 /******************************************************************************/
96 /* A c c e p t */
97 /******************************************************************************/
98 
99 int XrdNetSocket::Accept(int timeout)
100 {
101  int retc, ClientSock;
102 
103  ErrCode = 0;
104 
105  // Check if a timeout was requested
106  //
107  if (timeout >= 0)
108  {struct pollfd sfd = {SockFD,
109  POLLIN|POLLRDNORM|POLLRDBAND|POLLPRI|POLLHUP, 0};
110  do {retc = poll(&sfd, 1, timeout);}
111  while(retc < 0 && (errno == EAGAIN || errno == EINTR));
112  if (!sfd.revents) return -1;
113  }
114 
115  do {ClientSock = XrdSysFD_Accept(SockFD, (struct sockaddr *)0, 0);}
116  while(ClientSock < 0 && errno == EINTR);
117 
118  if (ClientSock < 0 && eroute) eroute->Emsg("Accept",errno,"accept connection");
119 
120  // Return the socket number.
121  //
122  return ClientSock;
123 }
124 
125 /******************************************************************************/
126 /* C l o s e */
127 /******************************************************************************/
128 
130 {
131  // Close any open file descriptor.
132  //
133  if (SockFD >= 0) {close(SockFD); SockFD=-1;}
134 
135  // Reset values and return.
136  //
137  ErrCode=0;
138 }
139 
140 /******************************************************************************/
141 /* C r e a t e */
142 /******************************************************************************/
143 
145  const char *fn, mode_t mode, int opts)
146 {
147  XrdNetSocket *ASock;
148  int pflags = (opts & XRDNET_FIFO ? S_IFIFO : S_IFSOCK);
149  int sflags = (opts & XRDNET_UDPSOCKET) | XRDNET_SERVER;
150  int rc = 0;
151  mode_t myMode = (mode & (S_IRWXU | S_IRWXG));
152  const char *eMsg = 0;
153  char fnbuff[1024] = {0};
154 
155 // Setup the path
156 //
157  if (!socketPath(Say, fnbuff, path, fn, mode|pflags))
158  return (XrdNetSocket *)0;
159 
160 // Connect to the path
161 //
162  ASock = new XrdNetSocket(Say);
163 #ifndef WIN32
164  if (opts & XRDNET_FIFO)
165  {if ((ASock->SockFD = mkfifo(fnbuff, mode)) < 0 && errno != EEXIST)
166  {eMsg = "create fifo"; rc = errno;}
167  else if ((ASock->SockFD = XrdSysFD_Open(fnbuff, O_RDWR, myMode)) < 0)
168  {eMsg = "open fifo"; rc = errno;}
169  else if (opts & XRDNET_NOCLOSEX) XrdSysFD_Yield(ASock->SockFD);
170  } else if (ASock->Open(fnbuff, -1, sflags) < 0)
171  {eMsg = "create socket"; rc = ASock->LastError();}
172 #else
173  if (ASock->Open(fnbuff, -1, sflags) < 0)
174  {eMsg = "create socket"; rc = ASock->LastError();}
175 #endif
176 
177 // Return the result
178 //
179  if (eMsg) {Say->Emsg("Create", rc, eMsg, fnbuff);
180  delete ASock; ASock = 0;
181  }
182  return ASock;
183 }
184 
185 /******************************************************************************/
186 /* D e t a c h */
187 /******************************************************************************/
188 
190 { int oldFD = SockFD;
191  SockFD = -1;
192  return oldFD;
193 }
194 
195 /******************************************************************************/
196 /* g e t W i n d o w */
197 /******************************************************************************/
198 
199 int XrdNetSocket::getWindow(int fd, int &Windowsz, XrdSysError *eDest)
200 {
201  socklen_t szb = (socklen_t)sizeof(Windowsz);
202 
203  if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, (Sokdata_t)&Windowsz, &szb))
204  {if (eDest) eDest->Emsg("setWindow", errno, "set socket RCVBUF");
205  return -1;
206  }
207  return 0;
208 }
209 
210 /******************************************************************************/
211 /* O p e n */
212 /******************************************************************************/
213 
214 int XrdNetSocket::Open(const char *inpath, int port, int flags, int windowsz)
215 {
216  const char *epath, *eText, *action = "configure socket";
217  char pbuff[128];
218  int myEC, backlog, SockProt;
219  int SockType = (flags & XRDNET_UDPSOCKET ? SOCK_DGRAM : SOCK_STREAM);
220  const int one = 1;
221  const SOCKLEN_t szone = (SOCKLEN_t)sizeof(one);
222 
223 // Supply actual port number in error messages
224 //
225  if (inpath) epath = inpath;
226  else {sprintf(pbuff, "port %d", port);
227  epath = pbuff;
228  }
229 
230 // Make sure this object is available for a new socket
231 //
232  if (SockFD >= 0) return Err(Open, EBUSY, "create socket for", epath);
233 
234 // Save the request flags, sometimes we need to check them from the local copy
235 //
236  myEC = ErrCode = 0;
237 
238 // Preset out address information
239 //
240  if ((eText = SockInfo.Set(inpath,(port < 0 ? XrdNetAddr::PortInSpec:port))))
241  {ErrCode = EHOSTUNREACH;
242  if (eroute)
243  {char buff[512];
244  snprintf(buff,sizeof(buff),"'%s'; %c%s",epath,tolower(*eText),eText+1);
245  eroute->Emsg("Open", "Unable to create socket for", buff);
246  }
247  return -1;
248  }
249 
250 // Allocate a socket descriptor of the right type
251 //
252  SockProt = SockInfo.Protocol();
253  if ((SockFD = XrdSysFD_Socket(SockProt, SockType, 0)) < 0)
254  return Err(Open, errno, "create socket for", epath);
255 
256 // Based on the type socket, set appropriate options. For server-side Unix
257 // sockets we must unlink the corresponding Unix path name or bind will fail.
258 // In some OS's, this creates a problem (e.g., Solaris) since the file inode is
259 // used to identify the socket and will likely change. This means that connects
260 // occuring before the bind will hang up to 3 minutes and client needs to retry.
261 // For non-Unix socketsr be prepared to timeout connects and try again.
262 //
263  if (SockProt == PF_UNIX)
264  {setOpts(SockFD, flags | XRDNET_UDPSOCKET, eroute);
265  if (flags & XRDNET_SERVER) unlink((const char *)inpath);
266  } else {
267  setOpts(SockFD, flags, eroute);
268  if (setsockopt(SockFD,SOL_SOCKET,SO_REUSEADDR, (Sokdata_t)&one, szone)
269  && eroute) eroute->Emsg("Open",errno,"set socket REUSEADDR for",epath);
270  }
271 
272 // Set the window size or udp buffer size, as needed (ignore errors)
273 //
274  if (windowsz) setWindow(SockFD, windowsz, eroute);
275 
276 // Either do a bind or a connect.
277 //
278  if (flags & XRDNET_SERVER)
279  {action = "bind socket to";
280  if (bind(SockFD, SockInfo.SockAddr(), SockInfo.SockSize())) myEC = errno;
281  else if (SockType == SOCK_STREAM)
282  {action = "listen on stream";
283  if (!(backlog = flags & XRDNET_BKLG))
284  backlog = XRDNETSOCKET_MAXBKLG;
285  if (listen(SockFD, backlog)) myEC = errno;
286  }
287  if (SockProt == PF_UNIX) chmod(inpath, S_IRWXU);
288  } else {
289  int tmo = flags & XRDNET_TOUT;
290  action = "connect socket to";
291  if (SockType == SOCK_STREAM && tmo)
292  myEC = XrdNetConnect::Connect(SockFD, SockInfo.SockAddr(),
293  SockInfo.SockSize(), tmo);
294  else if (connect(SockFD,SockInfo.SockAddr(),SockInfo.SockSize()))
295  myEC = errno;
296  }
297 
298 // Check for any errors and return (Close() sets SockFD to -1).
299 //
300  if (myEC)
301  {Close();
302  ErrCode = myEC;
303  if (!(flags & XRDNET_NOEMSG) && eroute)
304  eroute->Emsg("Open", ErrCode, action, epath);
305  }
306  return SockFD;
307 }
308 
309 /******************************************************************************/
310 /* P e e r n a m e */
311 /******************************************************************************/
312 
313 const char *XrdNetSocket::Peername(const struct sockaddr **InetAddr,
314  int *InetSize)
315 {
316  const char *errtxt, *PeerName;
317 
318 // Make sure we have something to look at
319 //
320  if (SockFD < 0)
321  {if (eroute) eroute->Emsg("Peername",
322  "Unable to obtain peer name; socket not open");
323  return (char *)0;
324  }
325 
326 // Get the host name on the other side of this socket
327 //
328  if (!(PeerName = SockInfo.Name(0, &errtxt)))
329  {if (eroute)
330  eroute->Emsg("Peername", "Unable to obtain peer name; ",errtxt);
331  ErrCode = ESRCH;
332  }
333 
334 // Return possible address, length and the name
335 //
336  if (InetAddr) *InetAddr = SockInfo.SockAddr();
337  if (InetSize) *InetSize = SockInfo.SockSize();
338  return PeerName;
339 }
340 
341 /******************************************************************************/
342 /* s e t O p t s */
343 /******************************************************************************/
344 
346 {
347  int rc = 0;
348  const int one = 1;
349 #if defined(__linux__) || defined(__GNU__)
350  const int szint = sizeof(int);
351 #endif
352  const SOCKLEN_t szone = (SOCKLEN_t)sizeof(one);
353  static int tcpprotid = XrdNetUtils::ProtoID("tcp");
354  static struct linger liopts = {1, XRDNETSOCKET_LINGER};
355  const SOCKLEN_t szlio = (SOCKLEN_t)sizeof(liopts);
356 
357  if (opts & XRDNET_NOCLOSEX && !XrdSysFD_Yield(xfd))
358  {rc = 1;
359  if (eDest) eDest->Emsg("setOpts", errno, "set fd close on exec");
360  }
361 
362  if (opts & XRDNET_UDPSOCKET) return rc;
363 
364  if (!(opts & XRDNET_NOLINGER)
365  && setsockopt(xfd,SOL_SOCKET,SO_LINGER,(Sokdata_t)&liopts,szlio))
366  {rc = 1;
367  if (eDest) eDest->Emsg("setOpts", errno, "set socket LINGER");
368  }
369 
370  if (opts & XRDNET_KEEPALIVE)
371  {if (setsockopt(xfd,SOL_SOCKET,SO_KEEPALIVE,(Sokdata_t)&one,szone))
372  {rc = 1;
373  if (eDest) eDest->Emsg("setOpts", errno, "set socket KEEPALIVE");
374  }
375 #if defined(__linux__) || defined(__GNU__)
376  else if (opts & XRDNET_SERVER) // Following are inherited in Linux
378  && setsockopt(xfd,SOL_TCP,TCP_KEEPIDLE,&XrdNetSocketCFG::ka_Idle,szint))
379  {rc = 1;
380  if (eDest) eDest->Emsg("setOpts", errno, "set socket KEEPIDLE");
381  }
383  && setsockopt(xfd,SOL_TCP,TCP_KEEPINTVL,&XrdNetSocketCFG::ka_Itvl,szint))
384  {rc = 1;
385  if (eDest) eDest->Emsg("setOpts", errno, "set socket KEEPINTVL");
386  }
388  && setsockopt(xfd,SOL_TCP,TCP_KEEPCNT, &XrdNetSocketCFG::ka_Icnt,szint))
389  {rc = 1;
390  if (eDest) eDest->Emsg("setOpts", errno, "set socket KEEPCNT");
391  }
392  }
393 #endif
394  }
395 
396  if (!(opts & XRDNET_DELAY)
397  && setsockopt(xfd, tcpprotid, TCP_NODELAY, (Sokdata_t)&one,szone))
398  {rc = 1;
399  if (eDest) eDest->Emsg("setOpts", errno, "set socket NODELAY");
400  }
401 
402  return rc;
403 }
404 
405 /******************************************************************************/
406 /* s e t W i n d o w */
407 /******************************************************************************/
408 
409 int XrdNetSocket::setWindow(int xfd, int Windowsz, XrdSysError *eDest)
410 {
411  int rc = 0;
412  const SOCKLEN_t szwb = (SOCKLEN_t)sizeof(Windowsz);
413 
414  if (setsockopt(xfd, SOL_SOCKET, SO_SNDBUF,
415  (Sokdata_t)&Windowsz, szwb))
416  {rc = 1;
417  if (eDest) eDest->Emsg("setWindow", errno, "set socket SNDBUF");
418  }
419 
420  if (setsockopt(xfd, SOL_SOCKET, SO_RCVBUF,
421  (Sokdata_t)&Windowsz, szwb))
422  {rc = 1;
423  if (eDest) eDest->Emsg("setWindow", errno, "set socket RCVBUF");
424  }
425  return rc;
426 }
427 
428 /******************************************************************************/
429 /* S o c k D a t a */
430 /******************************************************************************/
431 
433 {
434  const char *errtxt, *Name;
435 
436 // Make sure we have something to look at
437 //
438  if (SockFD < 0)
439  {if (eroute) eroute->Emsg("SockInfo",
440  "Unable to obtain socket info; socket not open");
441  return (const char *)0;
442  }
443 
444 // Return possible address, length and the name
445 //
446  const XrdNetSockAddr *netSA = SockInfo.NetAddr();
447  if (!netSA)
448  {if (eroute)
449  {eroute->Emsg("SockInfo", "Unable to obtain socker info; "
450  "not an network socket.");
451  ErrCode = EOPNOTSUPP;
452  return (const char *)0;
453  }
454  }
455  memcpy(&InetAddr, netSA, sizeof(XrdNetSockAddr));
456 
457 // Get the host name on the other side of this socket
458 //
459  if (!(Name = SockInfo.Name(0, &errtxt)))
460  {if (eroute)
461  eroute->Emsg("SockInfo", "Unable to obtain socker info; ",errtxt);
462  ErrCode = ESRCH;
463  return (const char *)0;
464  }
465 
466 // All done
467 //
468  return Name;
469 }
470 
471 /******************************************************************************/
472 /* S o c k N a m e */
473 /******************************************************************************/
474 
475 int XrdNetSocket::SockName(char *buff, int blen)
476 {
477 
478 // Make sure we have something here
479 //
480  if (SockFD < 0) {*buff = 0; return ENOTSOCK;}
481 
482 // Format the name
483 //
484  if (!SockInfo.Format(buff, blen)) return EINVAL;
485  return 0;
486 }
487 
488 /******************************************************************************/
489 /* s o c k e t P a t h */
490 /******************************************************************************/
491 
493  const char *path, const char *fn, mode_t mode)
494 {
495  const int srchOK = S_IXUSR | S_IXGRP;
496  const int sfMask = (S_IFIFO | S_IFSOCK);
497  int rc, i, fnlen = strlen(fnbuff);
498  mode_t myMode = (mode & (S_IRWXU | S_IRWXG)) | srchOK;
499  struct stat buf;
500  char *sp = 0;
501 
502 // Copy the const char path because makePath modifies it
503 //
504  i = strlen(path);
505  if (strlcpy(fnbuff, path, 1024) >= 1024 || (i + fnlen + 1) >= 1024)
506  {Say->Emsg("createPath", "Socket path", path, "too long");
507  return 0;
508  }
509 
510 // Check if we should separate the filename from the path
511 //
512  if (!fn)
513  {if (fnbuff[i-1] == '/') fnbuff[i-1] = '\0';
514  if ((sp = rindex(fnbuff, '/'))) *sp = '\0';
515  }
516 
517 // Create the directory if it is not already there
518 //
519  if ((rc = XrdOucUtils::makePath(fnbuff, myMode)))
520  {Say->Emsg("createPath", errno, "create path", path);
521  return 0;
522  }
523 
524 // Construct full filename
525 //
526  if (sp) *sp = '/';
527  else {if (path[i-1] != '/') fnbuff[i++] = '/';
528  if (fn) strcpy(fnbuff+i, fn);
529  }
530 
531 // Check is we have already created it and whether we can access
532 //
533  if (!stat(fnbuff,&buf))
534  {if ((buf.st_mode & S_IFMT) != (mode & sfMask))
535  {Say->Emsg("createPath","Path",fnbuff,
536  (mode & S_IFSOCK) ? "exists but is not a socket"
537  : "exists but is not a pipe");
538  return 0;
539  }
540  if (access(fnbuff, W_OK))
541  {Say->Emsg("createPath", errno, "access path", fnbuff);
542  return 0;
543  }
544  } else chmod(fnbuff, mode); // This may fail on some platforms
545 
546 // All set now
547 //
548  return fnbuff;
549 }
static XrdSysError eDest(0,"crypto_")
#define XRDNET_TOUT
Definition: XrdNetOpts.hh:110
#define XRDNET_KEEPALIVE
Definition: XrdNetOpts.hh:63
#define XRDNET_FIFO
Definition: XrdNetOpts.hh:83
#define XRDNET_NOCLOSEX
Definition: XrdNetOpts.hh:67
#define XRDNET_SERVER
Definition: XrdNetOpts.hh:99
#define XRDNET_NOLINGER
Definition: XrdNetOpts.hh:75
#define XRDNET_NOEMSG
Definition: XrdNetOpts.hh:71
#define XRDNET_UDPSOCKET
Definition: XrdNetOpts.hh:79
#define XRDNET_DELAY
Definition: XrdNetOpts.hh:59
#define XRDNET_BKLG
Definition: XrdNetOpts.hh:104
#define XRDNETSOCKET_MAXBKLG
Definition: XrdNetOpts.hh:118
#define XRDNETSOCKET_LINGER
Definition: XrdNetOpts.hh:122
#define Err(p, a, b, c)
Definition: XrdNetSocket.cc:81
int unlink(const char *path)
int access(const char *path, int amode)
#define close(a)
Definition: XrdPosix.hh:48
#define stat(a, b)
Definition: XrdPosix.hh:101
#define eMsg(x)
struct myOpts opts
size_t strlcpy(char *dst, const char *src, size_t sz)
#define SOCKLEN_t
#define Sokdata_t
const sockaddr * SockAddr()
const XrdNetSockAddr * NetAddr()
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
SOCKLEN_t SockSize()
const char * Name(const char *eName=0, const char **eText=0)
static const int PortInSpec
Definition: XrdNetAddr.hh:112
const char * Set(const char *hSpec, int pNum=PortInSpec)
Definition: XrdNetAddr.cc:216
static int Connect(int fd, const struct sockaddr *name, int namelen, int tsec=-1)
const char * SockData(XrdNetSockAddr &InetAddr)
static int setWindow(int fd, int Windowsz, XrdSysError *eDest=0)
int SockName(char *buff, int blen)
XrdNetSocket(XrdSysError *erobj=0, int SockFileDesc=-1)
Definition: XrdNetSocket.cc:88
int Open(const char *path, int port=-1, int flags=0, int sockbuffsz=0)
static int setOpts(int fd, int options, XrdSysError *eDest=0)
const char * Peername(const struct sockaddr **InetAddr=0, int *InetSize=0)
static char * socketPath(XrdSysError *Say, char *inbuff, const char *path, const char *fn, mode_t mode)
int Accept(int ms=-1)
Definition: XrdNetSocket.cc:99
static int getWindow(int fd, int &Windowsz, XrdSysError *eDest=0)
static XrdNetSocket * Create(XrdSysError *Say, const char *path, const char *fn, mode_t mode, int isudp=0)
static int ProtoID(const char *pName)
Definition: XrdNetUtils.cc:838
static int makePath(char *path, mode_t mode, bool reset=false)
Definition: XrdOucUtils.cc:994
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
XrdSysError Say