1 | /************************************************************************* |
2 | * libjson-rpc-cpp |
3 | ************************************************************************* |
4 | * @file linuxtcpsocketclient.cpp |
5 | * @date 17.10.2016 |
6 | * @author Alexandre Poirot <alexandre.poirot@legrand.fr> |
7 | * @license See attached LICENSE.txt |
8 | ************************************************************************/ |
9 | |
10 | #include "linuxtcpsocketclient.h" |
11 | #include "../../common/sharedconstants.h" |
12 | #include "../../common/streamreader.h" |
13 | #include "../../common/streamwriter.h" |
14 | #include <arpa/inet.h> |
15 | #include <cstdio> |
16 | #include <cstdlib> |
17 | #include <cstring> |
18 | #include <errno.h> |
19 | #include <iostream> |
20 | #include <netdb.h> |
21 | #include <netinet/in.h> |
22 | #include <string.h> |
23 | #include <sys/socket.h> |
24 | #include <unistd.h> |
25 | |
26 | using namespace jsonrpc; |
27 | using namespace std; |
28 | |
29 | LinuxTcpSocketClient::LinuxTcpSocketClient(const std::string &hostToConnect, const unsigned int &port) : hostToConnect(hostToConnect), port(port) {} |
30 | |
31 | LinuxTcpSocketClient::~LinuxTcpSocketClient() {} |
32 | |
33 | void LinuxTcpSocketClient::SendRPCMessage(const std::string &message, std::string &result) { |
34 | int socket_fd = this->Connect(); |
35 | |
36 | StreamWriter writer; |
37 | string toSend = message + DEFAULT_DELIMITER_CHAR; |
38 | if (!writer.Write(toSend, socket_fd)) { |
39 | throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Could not write request" ); |
40 | } |
41 | |
42 | StreamReader reader(DEFAULT_BUFFER_SIZE); |
43 | if (!reader.Read(result, socket_fd, DEFAULT_DELIMITER_CHAR)) { |
44 | throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Could not read response" ); |
45 | } |
46 | close(socket_fd); |
47 | } |
48 | |
49 | int LinuxTcpSocketClient::Connect() { |
50 | if (this->IsIpv4Address(this->hostToConnect)) { |
51 | return this->Connect(this->hostToConnect, this->port); |
52 | } else // We were given a hostname |
53 | { |
54 | struct addrinfo *result = NULL; |
55 | struct addrinfo hints; |
56 | memset(&hints, 0, sizeof(struct addrinfo)); |
57 | hints.ai_family = AF_INET; |
58 | hints.ai_socktype = SOCK_STREAM; |
59 | hints.ai_protocol = IPPROTO_TCP; |
60 | char port[6]; |
61 | snprintf(port, 6, "%d" , this->port); |
62 | int retval = getaddrinfo(this->hostToConnect.c_str(), port, &hints, &result); |
63 | if (retval != 0) |
64 | throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Could not resolve hostname." ); |
65 | bool foundValidIp = false; |
66 | int socket_fd; |
67 | for (struct addrinfo *temp = result; (temp != NULL) && !foundValidIp; temp = temp->ai_next) { |
68 | if (temp->ai_family == AF_INET) { |
69 | try { |
70 | sockaddr_in *sock = reinterpret_cast<sockaddr_in *>(temp->ai_addr); |
71 | socket_fd = this->Connect(inet_ntoa(sock->sin_addr), ntohs(sock->sin_port)); |
72 | foundValidIp = true; |
73 | } catch (const JsonRpcException &e) { |
74 | foundValidIp = false; |
75 | socket_fd = -1; |
76 | } catch (void *p) { |
77 | foundValidIp = false; |
78 | socket_fd = -1; |
79 | } |
80 | } |
81 | } |
82 | |
83 | if (!foundValidIp) |
84 | throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, "Hostname resolved but connection was refused on the given port." ); |
85 | |
86 | return socket_fd; |
87 | } |
88 | } |
89 | |
90 | int LinuxTcpSocketClient::Connect(const string &ip, const int &port) { |
91 | sockaddr_in address; |
92 | int socket_fd; |
93 | socket_fd = socket(AF_INET, SOCK_STREAM, 0); |
94 | if (socket_fd < 0) { |
95 | string message = "socket() failed" ; |
96 | int err = errno; |
97 | switch (err) { |
98 | case EACCES: |
99 | case EAFNOSUPPORT: |
100 | case EINVAL: |
101 | case EMFILE: |
102 | case ENOBUFS: |
103 | case ENOMEM: |
104 | case EPROTONOSUPPORT: |
105 | message = strerror(err); |
106 | break; |
107 | } |
108 | throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); |
109 | } |
110 | memset(&address, 0, sizeof(sockaddr_in)); |
111 | |
112 | address.sin_family = AF_INET; |
113 | inet_aton(ip.c_str(), &(address.sin_addr)); |
114 | address.sin_port = htons(port); |
115 | |
116 | if (connect(socket_fd, (struct sockaddr *)&address, sizeof(sockaddr_in)) != 0) { |
117 | string message = "connect() failed" ; |
118 | int err = errno; |
119 | switch (err) { |
120 | case EACCES: |
121 | case EPERM: |
122 | case EADDRINUSE: |
123 | case EAFNOSUPPORT: |
124 | case EAGAIN: |
125 | case EALREADY: |
126 | case EBADF: |
127 | case ECONNREFUSED: |
128 | case EFAULT: |
129 | case EINPROGRESS: |
130 | case EINTR: |
131 | case EISCONN: |
132 | case ENETUNREACH: |
133 | case ENOTSOCK: |
134 | case ETIMEDOUT: |
135 | message = strerror(err); |
136 | break; |
137 | } |
138 | close(socket_fd); |
139 | throw JsonRpcException(Errors::ERROR_CLIENT_CONNECTOR, message); |
140 | } |
141 | return socket_fd; |
142 | } |
143 | |
144 | bool LinuxTcpSocketClient::IsIpv4Address(const std::string &ip) { |
145 | struct in_addr addr; |
146 | return (inet_aton(ip.c_str(), &addr) != 0); |
147 | } |
148 | |