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
26using namespace jsonrpc;
27using namespace std;
28
29LinuxTcpSocketClient::LinuxTcpSocketClient(const std::string &hostToConnect, const unsigned int &port) : hostToConnect(hostToConnect), port(port) {}
30
31LinuxTcpSocketClient::~LinuxTcpSocketClient() {}
32
33void 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
49int 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
90int 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
144bool LinuxTcpSocketClient::IsIpv4Address(const std::string &ip) {
145 struct in_addr addr;
146 return (inet_aton(ip.c_str(), &addr) != 0);
147}
148