1 // A simple standalone XML-RPC server written in C++.
2 //
3 // This server returns to the caller his IP address and port number,
4 // as a demonstration of how to access such information.
5 //
6 // This works only on Unix (to wit, something that uses Abyss's
7 // ChanSwitchUnix channel switch to accept TCP connections from clients).
8 //
9 // See xmlrpc_sample_add_server.cpp for a more basic example.
10 //
11 //    To run this:
12 //
13 //       $ ./callinfo_abyss_server &
14 //       $ xmlrpc localhost:8080 getCallInfo
15 
16 #define WIN32_LEAN_AND_MEAN  /* required by xmlrpc-c/server_abyss.hpp */
17 
18 #include <cassert>
19 #include <stdexcept>
20 #include <iostream>
21 #include <unistd.h>
22 #include <stdio.h>
23 #ifndef _WIN32
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #endif
27 
28 using namespace std;
29 
30 #include <xmlrpc-c/base.hpp>
31 #include <xmlrpc-c/registry.hpp>
32 #include <xmlrpc-c/server_abyss.hpp>
33 #include <xmlrpc-c/abyss.h>
34 
35 
36 struct tcpPortAddr {
37     unsigned char  ipAddr[4];
38     unsigned short portNumber;
39 };
40 
41 
42 static struct tcpPortAddr
tcpAddrFromSockAddr(struct sockaddr const sockAddr)43 tcpAddrFromSockAddr(struct sockaddr const sockAddr) {
44 
45     const struct sockaddr_in * const sockAddrInP(
46         static_cast<struct sockaddr_in *>((void *)&sockAddr));
47 
48     const unsigned char * const ipAddr(
49         static_cast<const unsigned char *>(
50             (const void *)&sockAddrInP->sin_addr.s_addr)
51         );   // 4 byte array
52 
53     assert(sockAddrInP->sin_family == AF_INET);
54 
55     struct tcpPortAddr retval;
56 
57     retval.ipAddr[0] = ipAddr[0];
58     retval.ipAddr[1] = ipAddr[1];
59     retval.ipAddr[2] = ipAddr[2];
60     retval.ipAddr[3] = ipAddr[3];
61     retval.portNumber = ntohs(sockAddrInP->sin_port);
62 
63     return retval;
64 }
65 
66 
67 
68 /* On Windows, we have struct abyss_win_chaninfo, while on Unix we have
69    struct abyss_unix_chaninfo, but for what we're doing here, they're
70    fungible -- we use only members that exist in both.  So we refer to the
71    generically with macro CHANINFO_TYPE.
72 */
73 
74 #ifdef _WIN32
75   #define CHANINFO_TYPE abyss_win_chaninfo
76 #else
77   #define CHANINFO_TYPE abyss_unix_chaninfo
78 #endif
79 
80 static string
rpcIpAddrMsg(xmlrpc_c::callInfo_serverAbyss const & callInfo)81 rpcIpAddrMsg(xmlrpc_c::callInfo_serverAbyss const& callInfo) {
82 
83     void * chanInfoPtr;
84     SessionGetChannelInfo(callInfo.abyssSessionP, &chanInfoPtr);
85 
86     struct CHANINFO_TYPE * const chanInfoP(
87         static_cast<struct CHANINFO_TYPE *>(chanInfoPtr));
88 
89     struct tcpPortAddr const tcpAddr(tcpAddrFromSockAddr(chanInfoP->peerAddr));
90 
91     char msg[128];
92 
93     sprintf(msg, "RPC is from IP address %u.%u.%u.%u, Port %hu",
94             tcpAddr.ipAddr[0],
95             tcpAddr.ipAddr[1],
96             tcpAddr.ipAddr[2],
97             tcpAddr.ipAddr[3],
98             tcpAddr.portNumber);
99 
100     return string(msg);
101 }
102 
103 
104 
105 class getCallInfoMethod : public xmlrpc_c::method2 {
106 public:
107     void
execute(xmlrpc_c::paramList const & paramList,const xmlrpc_c::callInfo * const callInfoPtr,xmlrpc_c::value * const retvalP)108     execute(xmlrpc_c::paramList        const& paramList,
109             const xmlrpc_c::callInfo * const  callInfoPtr,
110             xmlrpc_c::value *          const  retvalP) {
111 
112         const xmlrpc_c::callInfo_serverAbyss * const callInfoP(
113             dynamic_cast<const xmlrpc_c::callInfo_serverAbyss *>(callInfoPtr));
114 
115         paramList.verifyEnd(0);
116 
117         // Because this gets called via a xmlrpc_c::serverAbyss:
118         assert(callInfoP != NULL);
119 
120         *retvalP = xmlrpc_c::value_string(rpcIpAddrMsg(*callInfoP));
121     }
122 };
123 
124 
125 
126 int
main(int const,const char ** const)127 main(int           const,
128      const char ** const) {
129 
130     try {
131         xmlrpc_c::registry myRegistry;
132 
133         xmlrpc_c::methodPtr const getCallInfoMethodP(new getCallInfoMethod);
134 
135         myRegistry.addMethod("getCallInfo", getCallInfoMethodP);
136 
137         xmlrpc_c::serverAbyss myAbyssServer(xmlrpc_c::serverAbyss::constrOpt()
138                                             .registryP(&myRegistry)
139                                             .portNumber(8080)
140                                             );
141 
142         myAbyssServer.run();
143         // xmlrpc_c::serverAbyss.run() never returns
144         assert(false);
145     } catch (exception const& e) {
146         cerr << "Something failed.  " << e.what() << endl;
147     }
148     return 0;
149 }
150