reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
XmlRpcServerBase.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5#include "rutil/ResipAssert.h"
6
7#include <rutil/Data.hxx>
8#include <rutil/Socket.hxx>
11#include <rutil/Logger.hxx>
12#include <resip/stack/Tuple.hxx>
13#include <rutil/DnsUtil.hxx>
14#include <rutil/ParseBuffer.hxx>
16
20
21using namespace repro;
22using namespace resip;
23using namespace std;
24
25#define RESIPROCATE_SUBSYSTEM Subsystem::REPRO
26
27
31
32void
34{
35 mRpc->buildFdSet(fdset);
36};
37
38void
40{
41 mRpc->process(fdset);
42};
43
44bool
46{
47 return mRpc->isSane();
48}
49
51 mHandler(h),
52 mSane(true)
53{
54}
55
59
62 mTuple(ipAddr,port,ipVer,TCP,Data::Empty)
63{
64#ifdef USE_IPV6
65 mFd = ::socket(ipVer == V4 ? PF_INET : PF_INET6, SOCK_STREAM, 0);
66#else
67 mFd = ::socket(PF_INET, SOCK_STREAM, 0);
68#endif
69
70 if (mFd == INVALID_SOCKET)
71 {
72 int e = getErrno();
74 ErrLog(<< "XmlRpcServerBase::XmlRpcServerBase: Failed to create socket: " << strError(e));
75 setSane(false);
76 return;
77 }
78
79 DebugLog (<< "XmlRpcServerBase::XmlRpcServerBase: Creating fd=" << (int)mFd
80 << (ipVer == V4 ? " V4/" : " V6/") );
81
82 int on = 1;
83#if !defined(WIN32)
84 if (::setsockopt(mFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
85#else
86 if (::setsockopt(mFd, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)))
87#endif
88 {
89 int e = getErrno();
91 ErrLog(<< "XmlRpcServerBase::XmlRpcServerBase: Couldn't set sockoptions SO_REUSEADDR: " << strError(e));
92 setSane(false);
93 return;
94 }
95
96#ifdef USE_IPV6
97#ifdef __linux__
98 if (ipVer == V6)
99 {
100 if ( ::setsockopt(mFd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) )
101 {
102 int e = getErrno();
104 ErrLog(<< "XmlRpcServerBase::XmlRpcServerBase: Couldn't set sockoptions IPV6_V6ONLY: " << strError(e));
105 setSane(false);
106 return;
107 }
108 }
109#endif
110#endif
111
112 DebugLog(<< "XmlRpcServerBase::XmlRpcServerBase: Binding to " << Tuple::inet_ntop(mTuple));
113
114 if (::bind( mFd, &mTuple.getMutableSockaddr(), mTuple.length()) == SOCKET_ERROR)
115 {
116 int e = getErrno();
118 if (e == EADDRINUSE)
119 {
120 ErrLog(<< "XmlRpcServerBase::XmlRpcServerBase: " << mTuple << " already in use ");
121 }
122 else
123 {
124 ErrLog(<< "XmlRpcServerBase::XmlRpcServerBase: Could not bind to " << mTuple);
125 }
126 setSane(false);
127 return;
128 }
129
130 bool ok = makeSocketNonBlocking(mFd);
131 if (!ok)
132 {
133 int e = getErrno();
135 ErrLog(<< "XmlRpcServerBase::XmlRpcServerBase: Could not make HTTP socket non-blocking " << port);
136 setSane(false);
137 return;
138 }
139
140 // do the listen, seting the maximum queue size for completely established
141 // sockets -- on linux, tcp_max_syn_backlog should be used for the incomplete
142 // queue size(see man listen)
143 int e = listen(mFd,5);
144
145 if (e != 0)
146 {
147 int e = getErrno();
148 InfoLog(<< "XmlRpcServerBase::XmlRpcServerBase: Failed listen " << strError(e));
149 setSane(false);
150 return;
151 }
152}
153
155{
156#if defined(WIN32)
157 closesocket(mFd);
158#else
159 close(mFd);
160#endif
161 mFd = 0;
162 ConnectionMap::iterator it = mConnections.begin();
163 for(; it != mConnections.end(); it++)
164 {
165 delete it->second;
166 }
167}
168
169
170void
172{
174
175 ConnectionMap::iterator it = mConnections.begin();
176 for(; it != mConnections.end(); it++)
177 {
178 it->second->buildFdSet(fdset);
179 }
180}
181
182void
184{
186 fdset.setRead(mFd); // listen socket for server
187}
188
189void
191{
192 //StackLog(<<"process invoked");
193 // Process Response fifo first
194 while (mResponseFifo.messageAvailable())
195 {
196 StackLog(<<"outgoing message found");
197 ResponseInfo* responseInfo = mResponseFifo.getNext();
198 if(responseInfo->getRequestId() == 0)
199 {
200 // This is an event and not a response - dispatch to appropriate connection or all connections
201 if(responseInfo->getConnectionId() == 0)
202 {
203 ConnectionMap::iterator it = mConnections.begin();
204 for(; it != mConnections.end(); it++)
205 {
206 it->second->sendEvent(responseInfo->getResponseData());
207 }
208 }
209 else
210 {
211 ConnectionMap::iterator it = mConnections.find(responseInfo->getConnectionId());
212 if(it != mConnections.end())
213 {
214 it->second->sendEvent(responseInfo->getResponseData());
215 }
216 }
217 }
218 else
219 {
220 // This is a response to a request - dispatch to appropriate connection
221 ConnectionMap::iterator it = mConnections.find(responseInfo->getConnectionId());
222 if(it != mConnections.end())
223 {
224 it->second->sendResponse(responseInfo->getRequestId(), responseInfo->getResponseData(), responseInfo->getIsFinal());
225 }
226 else
227 {
228 ErrLog(<<"failed to find connection " << responseInfo->getConnectionId());
229 }
230 }
231 delete responseInfo;
232 }
233
235
236 processIncoming(fdset);
237
238 // Call process on each connection
239 ConnectionMap::iterator it = mConnections.begin();
240 for(; it != mConnections.end(); )
241 {
242 bool ok = it->second->process(fdset);
243 if (!ok)
244 {
245 delete it->second;
246 mConnections.erase(it++);
247 }
248 else
249 {
250 it++;
251 }
252 }
253}
254
255void
257{
258 if (fdset.readyToRead(mFd))
259 {
260 Tuple tuple(mTuple);
261 struct sockaddr& peer = tuple.getMutableSockaddr();
262 socklen_t peerLen = tuple.length();
263 Socket sock = accept( mFd, &peer, &peerLen);
264 if (sock == SOCKET_ERROR)
265 {
266 int e = getErrno();
267 switch (e)
268 {
269 case EAGAIN:
270#if EAGAIN != EWOULDBLOCK
271 case EWOULDBLOCK: // Treat EGAIN and EWOULDBLOCK as the same: http://stackoverflow.com/questions/7003234/which-systems-define-eagain-and-ewouldblock-as-different-values
272#endif
273 return;
274 default:
276 ErrLog(<< "XmlRpcServerBase::process: Some error reading from socket: " << e);
277 }
278 return;
279 }
281
282 if(mConnections.size() == MaxConnections)
283 {
284 closeOldestConnection();
285 }
286
287 XmlRpcConnectionBase* connection = new XmlRpcSocketConnection(*this,sock);
288 mConnections[connection->getConnectionId()] = connection;
289
290 DebugLog (<< "XmlRpcServerBase::process: Received TCP connection as connection=" << connection->getConnectionId() << " fd=" << (int)sock);
291 }
292}
293
294void
295XmlRpcServerBase::sendResponse(unsigned int connectionId,
296 unsigned int requestId,
297 const Data& responseData,
298 bool isFinal)
299{
300 mResponseFifo.add(new ResponseInfo(connectionId, requestId, responseData, isFinal));
302}
303
304void
305XmlRpcServerBase::sendEvent(unsigned int connectionId,
306 const Data& eventData)
307{
308 mResponseFifo.add(new ResponseInfo(connectionId, 0 /* requestId */, eventData, true /* isFinal */));
310}
311
312bool
314{
315 return mSane;
316}
317
318void
319XmlRpcSocketServer::closeOldestConnection()
320{
321 if(mConnections.empty()) return;
322
323 // Oldest Connection is the one with the lowest Id
324 ConnectionMap::iterator lowestConnectionIdIt = mConnections.end();
325 ConnectionMap::iterator it = mConnections.begin();
326 for(; it != mConnections.end(); it++)
327 {
328 if(it->second->getConnectionId() < lowestConnectionIdIt->second->getConnectionId())
329 {
330 lowestConnectionIdIt = it;
331 }
332 }
333 delete lowestConnectionIdIt->second;
334 mConnections.erase(lowestConnectionIdIt);
335}
336
337void
339{
340 switch (e)
341 {
342 case EAGAIN:
343 InfoLog (<< "No data ready to read" << strError(e));
344 break;
345 case EINTR:
346 InfoLog (<< "The call was interrupted by a signal before any data was read : " << strError(e));
347 break;
348 case EIO:
349 InfoLog (<< "I/O error : " << strError(e));
350 break;
351 case EBADF:
352 InfoLog (<< "fd is not a valid file descriptor or is not open for reading : " << strError(e));
353 break;
354 case EINVAL:
355 InfoLog (<< "fd is attached to an object which is unsuitable for reading : " << strError(e));
356 break;
357 case EFAULT:
358 InfoLog (<< "buf is outside your accessible address space : " << strError(e));
359 break;
360
361#if defined(WIN32)
362 case WSAENETDOWN:
363 InfoLog (<<" The network subsystem has failed. ");
364 break;
365 case WSAEFAULT:
366 InfoLog (<<" The buf or from parameters are not part of the user address space, "
367 "or the fromlen parameter is too small to accommodate the peer address. ");
368 break;
369 case WSAEINTR:
370 InfoLog (<<" The (blocking) call was canceled through WSACancelBlockingCall. ");
371 break;
372 case WSAEINPROGRESS:
373 InfoLog (<<" A blocking Windows Sockets 1.1 call is in progress, or the "
374 "service provider is still processing a callback function. ");
375 break;
376 case WSAEINVAL:
377 InfoLog (<<" The socket has not been bound with bind, or an unknown flag was specified, "
378 "or MSG_OOB was specified for a socket with SO_OOBINLINE enabled, "
379 "or (for byte stream-style sockets only) len was zero or negative. ");
380 break;
381 case WSAEISCONN :
382 InfoLog (<<"The socket is connected. This function is not permitted with a connected socket, "
383 "whether the socket is connection-oriented or connectionless. ");
384 break;
385 case WSAENETRESET:
386 InfoLog (<<" The connection has been broken due to the keep-alive activity "
387 "detecting a failure while the operation was in progress. ");
388 break;
389 case WSAENOTSOCK :
390 InfoLog (<<"The descriptor is not a socket. ");
391 break;
392 case WSAEOPNOTSUPP:
393 InfoLog (<<" MSG_OOB was specified, but the socket is not stream-style such as type "
394 "SOCK_STREAM, OOB data is not supported in the communication domain associated with this socket, "
395 "or the socket is unidirectional and supports only send operations. ");
396 break;
397 case WSAESHUTDOWN:
398 InfoLog (<<"The socket has been shut down; it is not possible to recvfrom on a socket after "
399 "shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH. ");
400 break;
401 case WSAEMSGSIZE:
402 InfoLog (<<" The message was too large to fit into the specified buffer and was truncated. ");
403 break;
404 case WSAETIMEDOUT:
405 InfoLog (<<" The connection has been dropped, because of a network failure or because the "
406 "system on the other end went down without notice. ");
407 break;
408 case WSAECONNRESET :
409 InfoLog (<<"Connection reset ");
410 break;
411
412 case WSAEWOULDBLOCK:
413 DebugLog (<<"Would Block ");
414 break;
415
416 case WSAEHOSTUNREACH:
417 InfoLog (<<"A socket operation was attempted to an unreachable host ");
418 break;
420 InfoLog (<<"Either the application has not called WSAStartup or WSAStartup failed. "
421 "The application may be accessing a socket that the current active task does not own (that is, trying to share a socket between tasks),"
422 "or WSACleanup has been called too many times. ");
423 break;
424 case WSAEACCES:
425 InfoLog (<<"An attempt was made to access a socket in a way forbidden by its access permissions ");
426 break;
427 case WSAENOBUFS:
428 InfoLog (<<"An operation on a socket could not be performed because the system lacked sufficient "
429 "buffer space or because a queue was full");
430 break;
431 case WSAENOTCONN:
432 InfoLog (<<"A request to send or receive data was disallowed because the socket is not connected "
433 "and (when sending on a datagram socket using sendto) no address was supplied");
434 break;
435 case WSAECONNABORTED:
436 InfoLog (<<"An established connection was aborted by the software in your host computer, possibly "
437 "due to a data transmission time-out or protocol error");
438 break;
439 case WSAEADDRNOTAVAIL:
440 InfoLog (<<"The requested address is not valid in its context. This normally results from an attempt to "
441 "bind to an address that is not valid for the local computer");
442 break;
443 case WSAEAFNOSUPPORT:
444 InfoLog (<<"An address incompatible with the requested protocol was used");
445 break;
446 case WSAEDESTADDRREQ:
447 InfoLog (<<"A required address was omitted from an operation on a socket");
448 break;
449 case WSAENETUNREACH:
450 InfoLog (<<"A socket operation was attempted to an unreachable network");
451 break;
452
453#endif
454
455 default:
456 InfoLog (<< "Some other error (" << e << "): " << strError(e));
457 break;
458 }
459}
460
461#ifdef BUILD_QPID_PROTON
462
463XmlRpcProtonServer::XmlRpcProtonServer(XmlRpcHandler& h, const resip::Data& brokerUrl, bool broadcast) :
465 ProtonThreadBase::ProtonReceiverBase(brokerUrl.c_str(),
466 std::chrono::seconds(60), // FIXME configurable
467 std::chrono::seconds(2)),
468 mBrokerUrl(brokerUrl),
469 mBroadcast(false),
470 mReady(false)
471{
472 mQpidProtonThread.reset(new ProtonThreadBase());
473 mProtonSender.reset(new ProtonThreadBase::ProtonSenderBase(std::string(brokerUrl.c_str())));
474 InfoLog(<<"XmlRpcServerBase::XmlRpcServerBase: using Qpid Proton AMQP to send to " << brokerUrl);
475}
476
477XmlRpcProtonServer::~XmlRpcProtonServer()
478{
479 if(mQpidProtonThread.get())
480 {
481 // the thread may not have started or may have already been stopped
482 mQpidProtonThread->shutdown();
483 mQpidProtonThread->join();
484 return;
485 }
486}
487
488void
489XmlRpcProtonServer::buildFdSet(resip::FdSet& fdset)
490{
491 if(!mReady)
492 {
493 // can't do this in the constructor because shared_from_this() doesn't work there
494 if(!mBroadcast)
495 {
496 mQpidProtonThread->addReceiver(shared_from_this());
497 }
498 mQpidProtonThread->run();
499 mReady = true;
500 }
502}
503
504void
505XmlRpcProtonServer::processIncoming(resip::FdSet& fdset)
506{
507 StackLog(<<"processIncoming invoked");
508 // open incoming connections ?
510 while(fifo.messageAvailable())
511 {
512 try
513 {
514 std::unique_ptr<proton::message> m(fifo.getNext());
515 Data replyTo(m->reply_to());
516 StackLog(<<"reply to: " << m->reply_to());
517 auto it = mQueueConnection.find(replyTo);
518 unsigned int connectionId;
519 XmlRpcProtonConnection* connection = nullptr;
520 if(it == mQueueConnection.end())
521 {
522 StackLog(<<"new client");
523 std::shared_ptr<ProtonThreadBase::ProtonSenderBase> sender = mQpidProtonThread->getSenderForReply(m->reply_to());
524 connection = new XmlRpcProtonConnection(*this, replyTo, sender);
525 connectionId = connection->getConnectionId();
526 mConnections[connectionId] = connection;
527 mQueueConnection[replyTo] = connectionId;
528 }
529 else
530 {
531 connectionId = it->second;
532 StackLog(<<"existing client: " << connectionId);
533 connection = dynamic_cast<XmlRpcProtonConnection*>(mConnections[connectionId]);
534 if(!connection)
535 {
536 ErrLog(<<"invalid connectionId: " << connectionId << " for queue " << replyTo);
537 }
538 }
539 unsigned int requestId = connection->getNextRequestId();
540 Data requestBody = Data(proton::get<std::string>(m->body()));
541 connection->onRequest(requestId, requestBody);
542 StackLog(<<"message received: " << requestBody);
543 handleRequest(connectionId, requestId, requestBody);
544 StackLog(<<"message handled, requestId = " << requestId);
545 }
546 catch(...)
547 {
548 ErrLog(<<"exception while parsing the XML");
549 }
550 }
551}
552
553#endif // BUILD_QPID_PROTON
554
555
556/* ====================================================================
557 * The Vovida Software License, Version 1.0
558 *
559 * Copyright (c) 2000 Vovida Networks, Inc. All rights reserved.
560 * Copyright (c) 2010 SIP Spectrum, Inc. All rights reserved.
561 * Copyright (c) 2022 Daniel Pocock https://danielpocock.com
562 * Copyright (c) 2022 Software Freedom Institute LLC https://softwarefreedom.institute
563 *
564 * Redistribution and use in source and binary forms, with or without
565 * modification, are permitted provided that the following conditions
566 * are met:
567 *
568 * 1. Redistributions of source code must retain the above copyright
569 * notice, this list of conditions and the following disclaimer.
570 *
571 * 2. Redistributions in binary form must reproduce the above copyright
572 * notice, this list of conditions and the following disclaimer in
573 * the documentation and/or other materials provided with the
574 * distribution.
575 *
576 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
577 * and "Vovida Open Communication Application Library (VOCAL)" must
578 * not be used to endorse or promote products derived from this
579 * software without prior written permission. For written
580 * permission, please contact vocal@vovida.org.
581 *
582 * 4. Products derived from this software may not be called "VOCAL", nor
583 * may "VOCAL" appear in their name, without prior written
584 * permission of Vovida Networks, Inc.
585 *
586 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
587 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
588 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
589 * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL VOVIDA
590 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
591 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
592 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
593 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
594 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
595 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
596 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
597 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
598 * DAMAGE.
599 *
600 * ====================================================================
601 *
602 * This software consists of voluntary contributions made by Vovida
603 * Networks, Inc. and many individuals on behalf of Vovida Networks,
604 * Inc. For more information on Vovida Networks, Inc., please see
605 * <http://www.vovida.org/>.
606 *
607 */
608
#define DebugLog(args_)
Definition Logger.hxx:128
#define StackLog(args_)
Definition Logger.hxx:125
#define InfoLog(args_)
Definition Logger.hxx:131
#define ErrLog(args_)
Definition Logger.hxx:137
Handles cross-platform sockets compatibility.
#define WSANOTINITIALISED
Definition Socket.hxx:82
#define INVALID_SOCKET
Definition Socket.hxx:95
#define SOCKET_ERROR
Definition Socket.hxx:96
static int getErrno()
int Socket
bool getIsFinal() const noexcept
unsigned int getConnectionId() const noexcept
unsigned int getRequestId() const noexcept
const resip::Data & getResponseData() const noexcept
unsigned int getConnectionId() const
virtual void buildFdSet(resip::FdSet &fdset)
void process(resip::FdSet &fdset)
std::shared_ptr< XmlRpcServerBase > mRpc
XmlRpcServerBase(XmlRpcHandler &h)
void process(resip::FdSet &fdset)
resip::SelectInterruptor mSelectInterruptor
virtual void buildFdSet(resip::FdSet &fdset)
virtual void sendEvent(unsigned int connectionId, const resip::Data &eventData)
resip::Fifo< ResponseInfo > mResponseFifo
virtual void sendResponse(unsigned int connectionId, unsigned int requestId, const resip::Data &responseData, bool isFinal=true)
virtual void processIncoming(resip::FdSet &fdset)=0
XmlRpcSocketServer(XmlRpcHandler &h, int port, resip::IpVersion version, resip::Data ipAddr=resip::Data::Empty)
static void logSocketError(int e)
virtual void buildFdSet(resip::FdSet &fdset)
void processIncoming(resip::FdSet &fdset)
bool messageAvailable() const
is a message available?
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
Object-oriented wrapper for your platform's file-descriptor set.
Definition Socket.hxx:162
bool readyToRead(Socket fd)
Definition Socket.hxx:189
void setRead(Socket fd)
Definition Socket.hxx:204
void process(FdSet &fdset)
cleanup signalled fd
void interrupt()
cause the 'artificial' fd to signal
void buildFdSet(FdSet &fdset)
Used to add the 'artificial' fd to the fdset that will be responsible for interrupting a subsequent s...
Msg * getNext()
Returns the first message available.
Represents a network address.
Definition Tuple.hxx:59
sockaddr & getMutableSockaddr()
Retrieve the binary representation of the socket address for this tuple.
Definition Tuple.hxx:113
socklen_t length() const
Definition Tuple.cxx:655
static Data inet_ntop(const Tuple &tuple)
Converts the binary socket address to presentation format, via the DnsUtil::inet_ntop() method.
Definition Tuple.cxx:939
This Class is used to implement a primitive form of RPC using loose XML formatting.
dcm! – add UnusedChecking(_enum) below;
std::string strError(int err)
Definition compat.hxx:274
IpVersion
An enumeration of IP versions.
bool makeSocketNonBlocking(Socket fd)
Definition Socket.cxx:21