1 /*******************************************************************************
2 Copyright(c) 2017 Jasem Mutlaq. All rights reserved.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17 *******************************************************************************/
18
19 #include "connectiontcp.h"
20
21 #include "indilogger.h"
22 #include "indistandardproperty.h"
23
24 #include <cerrno>
25 #include <netdb.h>
26 #include <cstring>
27 #include <unistd.h>
28
29 #ifdef __FreeBSD__
30 #include <arpa/inet.h>
31 #include <netinet/in.h>
32 #include <sys/socket.h>
33 #endif
34
35 namespace Connection
36 {
37 extern const char *CONNECTION_TAB;
38
TCP(INDI::DefaultDevice * dev)39 TCP::TCP(INDI::DefaultDevice *dev) : Interface(dev, CONNECTION_TCP)
40 {
41 char defaultHostname[MAXINDINAME] = {0};
42 char defaultPort[MAXINDINAME] = {0};
43
44 // Try to load the port from the config file. If that fails, use default port.
45 IUGetConfigText(dev->getDeviceName(), INDI::SP::DEVICE_ADDRESS, "ADDRESS", defaultHostname, MAXINDINAME);
46 IUGetConfigText(dev->getDeviceName(), INDI::SP::DEVICE_ADDRESS, "PORT", defaultPort, MAXINDINAME);
47
48 // Address/Port
49 IUFillText(&AddressT[0], "ADDRESS", "Address", defaultHostname);
50 IUFillText(&AddressT[1], "PORT", "Port", defaultPort);
51 IUFillTextVector(&AddressTP, AddressT, 2, getDeviceName(), "DEVICE_ADDRESS", "Server", CONNECTION_TAB,
52 IP_RW, 60, IPS_IDLE);
53
54 IUFillSwitch(&TcpUdpS[0], "TCP", "TCP", ISS_ON);
55 IUFillSwitch(&TcpUdpS[1], "UDP", "UDP", ISS_OFF);
56 IUFillSwitchVector(&TcpUdpSP, TcpUdpS, 2, getDeviceName(), "CONNECTION_TYPE", "Connection Type",
57 CONNECTION_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
58
59 }
60
ISNewText(const char * dev,const char * name,char * texts[],char * names[],int n)61 bool TCP::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
62 {
63 if (!strcmp(dev, m_Device->getDeviceName()))
64 {
65 // TCP Server settings
66 if (!strcmp(name, AddressTP.name))
67 {
68 IUUpdateText(&AddressTP, texts, names, n);
69 AddressTP.s = IPS_OK;
70 IDSetText(&AddressTP, nullptr);
71 return true;
72 }
73 }
74
75 return false;
76 }
77
ISNewSwitch(const char * dev,const char * name,ISState * states,char * names[],int n)78 bool TCP::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
79 {
80 if (!strcmp(dev, m_Device->getDeviceName()))
81 {
82 if (!strcmp(name, TcpUdpSP.name))
83 {
84 IUUpdateSwitch(&TcpUdpSP, states, names, n);
85 TcpUdpSP.s = IPS_OK;
86
87 IDSetSwitch(&TcpUdpSP, nullptr);
88
89 return true;
90 }
91 }
92
93 return false;
94 }
95
Connect()96 bool TCP::Connect()
97 {
98 if (AddressT[0].text == nullptr || AddressT[0].text[0] == '\0' || AddressT[1].text == nullptr ||
99 AddressT[1].text[0] == '\0')
100 {
101 LOG_ERROR("Error! Server address is missing or invalid.");
102 return false;
103 }
104
105 const char *hostname = AddressT[0].text;
106 const char *port = AddressT[1].text;
107
108 LOGF_INFO("Connecting to %s@%s ...", hostname, port);
109
110 if (m_Device->isSimulation() == false)
111 {
112 struct sockaddr_in serv_addr;
113 struct hostent *hp = nullptr;
114 int ret = 0;
115
116 struct timeval ts;
117 ts.tv_sec = SOCKET_TIMEOUT;
118 ts.tv_usec = 0;
119
120 if (sockfd != -1)
121 close(sockfd);
122
123 // Lookup host name or IPv4 address
124 hp = gethostbyname(hostname);
125 if (!hp)
126 {
127 LOG_ERROR("Failed to lookup IP Address or hostname.");
128 return false;
129 }
130
131 memset(&serv_addr, 0, sizeof(serv_addr));
132 serv_addr.sin_family = AF_INET;
133 serv_addr.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr_list[0]))->s_addr;
134 serv_addr.sin_port = htons(atoi(port));
135
136 int socketType = 0;
137 if (TcpUdpS[0].s == ISS_ON)
138 {
139 socketType = SOCK_STREAM;
140 }
141 else
142 {
143 socketType = SOCK_DGRAM;
144 }
145
146 if ((sockfd = socket(AF_INET, socketType, 0)) < 0)
147 {
148 LOG_ERROR("Failed to create socket.");
149 return false;
150 }
151
152 // Connect to the mount
153 if ((ret = ::connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))) < 0)
154 {
155 LOGF_ERROR("Failed to connect to mount %s@%s: %s.", hostname, port, strerror(errno));
156 close(sockfd);
157 sockfd = -1;
158 return false;
159 }
160
161 // Set the socket receiving and sending timeouts
162 setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&ts, sizeof(struct timeval));
163 setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&ts, sizeof(struct timeval));
164 }
165
166 PortFD = sockfd;
167
168 LOG_DEBUG("Connection successful, attempting handshake...");
169 bool rc = Handshake();
170
171 if (rc)
172 {
173 LOGF_INFO("%s is online.", getDeviceName());
174 m_Device->saveConfig(true, "DEVICE_ADDRESS");
175 m_Device->saveConfig(true, "CONNECTION_TYPE");
176 }
177 else
178 LOG_DEBUG("Handshake failed.");
179
180 return rc;
181 }
182
Disconnect()183 bool TCP::Disconnect()
184 {
185 if (sockfd > 0)
186 {
187 close(sockfd);
188 sockfd = PortFD = -1;
189 }
190
191 return true;
192 }
193
Activated()194 void TCP::Activated()
195 {
196 m_Device->defineText(&AddressTP);
197 m_Device->defineSwitch(&TcpUdpSP);
198 m_Device->loadConfig(true, "DEVICE_ADDRESS");
199 m_Device->loadConfig(true, "CONNECTION_TYPE");
200 }
201
Deactivated()202 void TCP::Deactivated()
203 {
204 m_Device->deleteProperty(AddressTP.name);
205 m_Device->deleteProperty(TcpUdpSP.name);
206 }
207
saveConfigItems(FILE * fp)208 bool TCP::saveConfigItems(FILE *fp)
209 {
210 IUSaveConfigText(fp, &AddressTP);
211 IUSaveConfigSwitch(fp, &TcpUdpSP);
212
213 return true;
214 }
215
setDefaultHost(const char * addressHost)216 void TCP::setDefaultHost(const char *addressHost)
217 {
218 IUSaveText(&AddressT[0], addressHost);
219 }
220
setDefaultPort(uint32_t addressPort)221 void TCP::setDefaultPort(uint32_t addressPort)
222 {
223 char portStr[8];
224 snprintf(portStr, 8, "%d", addressPort);
225 IUSaveText(&AddressT[1], portStr);
226 }
227
setConnectionType(int type)228 void TCP::setConnectionType(int type)
229 {
230 IUResetSwitch(&TcpUdpSP);
231 TcpUdpS[type].s = ISS_ON;
232 IDSetSwitch(&TcpUdpSP, nullptr);
233 }
234 }
235