XRootD
Loading...
Searching...
No Matches
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
58#include "XrdNet/XrdNetOpts.hh"
60#include "XrdNet/XrdNetUtils.hh"
61#include "XrdOuc/XrdOucUtils.hh"
62#include "XrdSys/XrdSysError.hh"
63#include "XrdSys/XrdSysFD.hh"
65
66/******************************************************************************/
67/* G l o b a l s */
68/******************************************************************************/
69
70namespace 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
88XrdNetSocket::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
99int 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
199int 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
214int 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 if (SockType == SOCK_STREAM)
290 {int tmo = flags & XRDNET_TOUT;
291 action = "connect socket to";
292 if (tmo) 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
299// Check for any errors and return (Close() sets SockFD to -1).
300//
301 if (myEC)
302 {Close();
303 ErrCode = myEC;
304 if (!(flags & XRDNET_NOEMSG) && eroute)
305 eroute->Emsg("Open", ErrCode, action, epath);
306 }
307 return SockFD;
308}
309
310/******************************************************************************/
311/* P e e r n a m e */
312/******************************************************************************/
313
314const char *XrdNetSocket::Peername(const struct sockaddr **InetAddr,
315 int *InetSize)
316{
317 const char *errtxt, *PeerName;
318
319// Make sure we have something to look at
320//
321 if (SockFD < 0)
322 {if (eroute) eroute->Emsg("Peername",
323 "Unable to obtain peer name; socket not open");
324 return (char *)0;
325 }
326
327// Get the host name on the other side of this socket
328//
329 if (!(PeerName = SockInfo.Name(0, &errtxt)))
330 {if (eroute)
331 eroute->Emsg("Peername", "Unable to obtain peer name; ",errtxt);
332 ErrCode = ESRCH;
333 }
334
335// Return possible address, length and the name
336//
337 if (InetAddr) *InetAddr = SockInfo.SockAddr();
338 if (InetSize) *InetSize = SockInfo.SockSize();
339 return PeerName;
340}
341
342/******************************************************************************/
343/* s e t O p t s */
344/******************************************************************************/
345
347{
348 int rc = 0;
349 const int one = 1;
350#if defined(__linux__) || defined(__GNU__)
351 const int szint = sizeof(int);
352#endif
353 const SOCKLEN_t szone = (SOCKLEN_t)sizeof(one);
354 static int tcpprotid = XrdNetUtils::ProtoID("tcp");
355 static struct linger liopts = {1, XRDNETSOCKET_LINGER};
356 const SOCKLEN_t szlio = (SOCKLEN_t)sizeof(liopts);
357
358 if (opts & XRDNET_NOCLOSEX && !XrdSysFD_Yield(xfd))
359 {rc = 1;
360 if (eDest) eDest->Emsg("setOpts", errno, "set fd close on exec");
361 }
362
363 if (opts & XRDNET_UDPSOCKET) return rc;
364
365 if (!(opts & XRDNET_NOLINGER)
366 && setsockopt(xfd,SOL_SOCKET,SO_LINGER,(Sokdata_t)&liopts,szlio))
367 {rc = 1;
368 if (eDest) eDest->Emsg("setOpts", errno, "set socket LINGER");
369 }
370
372 {if (setsockopt(xfd,SOL_SOCKET,SO_KEEPALIVE,(Sokdata_t)&one,szone))
373 {rc = 1;
374 if (eDest) eDest->Emsg("setOpts", errno, "set socket KEEPALIVE");
375 }
376#if defined(__linux__) || defined(__GNU__)
377 else if (opts & XRDNET_SERVER) // Following are inherited in Linux
379 && setsockopt(xfd,SOL_TCP,TCP_KEEPIDLE,&XrdNetSocketCFG::ka_Idle,szint))
380 {rc = 1;
381 if (eDest) eDest->Emsg("setOpts", errno, "set socket KEEPIDLE");
382 }
384 && setsockopt(xfd,SOL_TCP,TCP_KEEPINTVL,&XrdNetSocketCFG::ka_Itvl,szint))
385 {rc = 1;
386 if (eDest) eDest->Emsg("setOpts", errno, "set socket KEEPINTVL");
387 }
389 && setsockopt(xfd,SOL_TCP,TCP_KEEPCNT, &XrdNetSocketCFG::ka_Icnt,szint))
390 {rc = 1;
391 if (eDest) eDest->Emsg("setOpts", errno, "set socket KEEPCNT");
392 }
393 }
394#endif
395 }
396
397 if (!(opts & XRDNET_DELAY)
398 && setsockopt(xfd, tcpprotid, TCP_NODELAY, (Sokdata_t)&one,szone))
399 {rc = 1;
400 if (eDest) eDest->Emsg("setOpts", errno, "set socket NODELAY");
401 }
402
403 return rc;
404}
405
406/******************************************************************************/
407/* s e t W i n d o w */
408/******************************************************************************/
409
410int XrdNetSocket::setWindow(int xfd, int Windowsz, XrdSysError *eDest)
411{
412 int rc = 0;
413 const SOCKLEN_t szwb = (SOCKLEN_t)sizeof(Windowsz);
414
415 if (setsockopt(xfd, SOL_SOCKET, SO_SNDBUF,
416 (Sokdata_t)&Windowsz, szwb))
417 {rc = 1;
418 if (eDest) eDest->Emsg("setWindow", errno, "set socket SNDBUF");
419 }
420
421 if (setsockopt(xfd, SOL_SOCKET, SO_RCVBUF,
422 (Sokdata_t)&Windowsz, szwb))
423 {rc = 1;
424 if (eDest) eDest->Emsg("setWindow", errno, "set socket RCVBUF");
425 }
426 return rc;
427}
428
429/******************************************************************************/
430/* S o c k N a m e */
431/******************************************************************************/
432
433int XrdNetSocket::SockName(char *buff, int blen)
434{
435
436// Make sure we have something here
437//
438 if (SockFD < 0) {*buff = 0; return ENOTSOCK;}
439
440// Format the name
441//
442 if (!SockInfo.Format(buff, blen)) return EINVAL;
443 return 0;
444}
445
446/******************************************************************************/
447/* s o c k e t P a t h */
448/******************************************************************************/
449
451 const char *path, const char *fn, mode_t mode)
452{
453 const int srchOK = S_IXUSR | S_IXGRP;
454 const int sfMask = (S_IFIFO | S_IFSOCK);
455 int rc, i, fnlen = strlen(fnbuff);
456 mode_t myMode = (mode & (S_IRWXU | S_IRWXG)) | srchOK;
457 struct stat buf;
458 char *sp = 0;
459
460// Copy the const char path because makePath modifies it
461//
462 i = strlen(path);
463 if (strlcpy(fnbuff, path, 1024) >= 1024 || (i + fnlen + 1) >= 1024)
464 {Say->Emsg("createPath", "Socket path", path, "too long");
465 return 0;
466 }
467
468// Check if we should separate the filename from the path
469//
470 if (!fn)
471 {if (fnbuff[i-1] == '/') fnbuff[i-1] = '\0';
472 if ((sp = rindex(fnbuff, '/'))) *sp = '\0';
473 }
474
475// Create the directory if it is not already there
476//
477 if ((rc = XrdOucUtils::makePath(fnbuff, myMode)))
478 {Say->Emsg("createPath", errno, "create path", path);
479 return 0;
480 }
481
482// Construct full filename
483//
484 if (sp) *sp = '/';
485 else {if (path[i-1] != '/') fnbuff[i++] = '/';
486 if (fn) strcpy(fnbuff+i, fn);
487 }
488
489// Check is we have already created it and whether we can access
490//
491 if (!stat(fnbuff,&buf))
492 {if ((buf.st_mode & S_IFMT) != (mode & sfMask))
493 {Say->Emsg("createPath","Path",fnbuff,
494 (mode & S_IFSOCK) ? "exists but is not a socket"
495 : "exists but is not a pipe");
496 return 0;
497 }
498 if (access(fnbuff, W_OK))
499 {Say->Emsg("createPath", errno, "access path", fnbuff);
500 return 0;
501 }
502 } else chmod(fnbuff, mode); // This may fail on some platforms
503
504// All set now
505//
506 return fnbuff;
507}
XrdOucPup XrdCmsParser::Pup & Say
static XrdSysError eDest(0,"crypto_")
#define XRDNET_TOUT
#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
#define XRDNETSOCKET_MAXBKLG
#define XRDNETSOCKET_LINGER
#define Err(p, a, b, c)
#define access(a, b)
Definition XrdPosix.hh:44
#define close(a)
Definition XrdPosix.hh:48
#define unlink(a)
Definition XrdPosix.hh:113
#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()
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
const char * Set(const char *hSpec, int pNum=PortInSpec)
static int Connect(int fd, const struct sockaddr *name, int namelen, int tsec=-1)
static int setWindow(int fd, int Windowsz, XrdSysError *eDest=0)
int SockName(char *buff, int blen)
XrdNetSocket(XrdSysError *erobj=0, int SockFileDesc=-1)
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)
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)
static int makePath(char *path, mode_t mode, bool reset=false)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)