SLikeNet 0.1.3

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
DynDNS.cpp
Go to the documentation of this file.
1 /*
2 * Original work: Copyright (c) 2014, Oculus VR, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * RakNet License.txt file in the licenses directory of this source tree. An additional grant
7 * of patent rights can be found in the RakNet Patents.txt file in the same directory.
8 *
9 *
10 * Modified work: Copyright (c) 2016-2018, SLikeSoft UG (haftungsbeschränkt)
11 *
12 * This source code was modified by SLikeSoft. Modifications are licensed under the MIT-style
13 * license found in the license.txt file in the root directory of this source tree.
14 */
15
16 #include "slikenet/NativeFeatureIncludes.h"
17 #if _RAKNET_SUPPORT_DynDNS==1 && _RAKNET_SUPPORT_TCPInterface==1
18
19 #include "slikenet/TCPInterface.h"
20 #include "slikenet/socket2.h"
21 #include "slikenet/DynDNS.h"
22 #include "slikenet/GetTime.h"
23 #include "slikenet/Base64Encoder.h"
24
25 using namespace SLNet;
26
27 struct DynDnsResult
28 {
29 const char *description;
30 const char *code;
31 DynDnsResultCode resultCode;
32 };
33
34 DynDnsResult resultTable[13] =
35 {
36 // See http://www.dyndns.com/developers/specs/flow.pdf
37 {"DNS update success.\nPlease wait up to 60 seconds for the change to take effect.\n", "good", RC_SUCCESS}, // Even with success, it takes time for the cache to update!
38 {"No change", "nochg", RC_NO_CHANGE},
39 {"Host has been blocked. You will need to contact DynDNS to reenable.", "abuse", RC_ABUSE},
40 {"Useragent is blocked", "badagent", RC_BAD_AGENT},
41 {"Username/password pair bad", "badauth", RC_BAD_AUTH},
42 {"Bad system parameter", "badsys", RC_BAD_SYS},
43 {"DNS inconsistency", "dnserr", RC_DNS_ERROR},
44 {"Paid account feature", "!donator", RC_NOT_DONATOR},
45 {"No such host in system", "nohost", RC_NO_HOST},
46 {"Invalid hostname format", "notfqdn", RC_NOT_FQDN},
47 {"Serious error", "numhost", RC_NUM_HOST},
48 {"This host exists, but does not belong to you", "!yours", RC_NOT_YOURS},
49 {"911", "911", RC_911}
50 };
51 DynDNS::DynDNS()
52 {
53 connectPhase=CP_IDLE;
54 tcp=0;
55 }
56 DynDNS::~DynDNS()
57 {
58 if (tcp)
59 SLNet::OP_DELETE(tcp, _FILE_AND_LINE_);
60 }
61 void DynDNS::Stop(void)
62 {
63 tcp->Stop();
64 connectPhase = CP_IDLE;
65 SLNet::OP_DELETE(tcp, _FILE_AND_LINE_);
66 tcp=0;
67 }
68
69
70 // newIPAddress is optional - if left out, DynDNS will use whatever it receives
71 void DynDNS::UpdateHostIPAsynch(const char *dnsHost, const char *newIPAddress, const char *usernameAndPassword )
72 {
73 myIPStr[0]=0;
74
75 if (tcp==0)
76 tcp = SLNet::OP_NEW<TCPInterface>(_FILE_AND_LINE_);
77 connectPhase = CP_IDLE;
78 host = dnsHost;
79
80 if (tcp->Start(0, 1)==false)
81 {
82 SetCompleted(RC_TCP_FAILED_TO_START, "TCP failed to start");
83 return;
84 }
85
86 connectPhase = CP_CONNECTING_TO_CHECKIP;
87 tcp->Connect("checkip.dyndns.org", 80, false);
88
89 // See https://www.dyndns.com/developers/specs/syntax.html
90 getString="GET /nic/update?hostname=";
91 getString+=dnsHost;
92 if (newIPAddress)
93 {
94 getString+="&myip=";
95 getString+=newIPAddress;
96 }
97 getString+="&wildcard=NOCHG&mx=NOCHG&backmx=NOCHG HTTP/1.0\n";
98 getString+="Host: members.dyndns.org\n";
99 getString+="Authorization: Basic ";
100 char outputData[512];
101 Base64Encoding((const unsigned char*) usernameAndPassword, (int) strlen(usernameAndPassword), outputData);
102 getString+=outputData;
103 getString+="User-Agent: Jenkins Software LLC - PC - 1.0\n\n";
104 }
105 void DynDNS::Update(void)
106 {
107 if (connectPhase==CP_IDLE)
108 return;
109
110 serverAddress=tcp->HasFailedConnectionAttempt();
111 if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS)
112 {
113 SetCompleted(RC_TCP_DID_NOT_CONNECT, "Could not connect to DynDNS");
114 return;
115 }
116
117 serverAddress=tcp->HasCompletedConnectionAttempt();
118 if (serverAddress!=UNASSIGNED_SYSTEM_ADDRESS)
119 {
120 if (connectPhase == CP_CONNECTING_TO_CHECKIP)
121 {
122 checkIpAddress=serverAddress;
123 connectPhase = CP_WAITING_FOR_CHECKIP_RESPONSE;
124 tcp->Send("GET\n\n", (unsigned int) strlen("GET\n\n"), serverAddress, false); // Needs 2 newlines! This is not documented and wasted a lot of my time
125 }
126 else
127 {
128 connectPhase = CP_WAITING_FOR_DYNDNS_RESPONSE;
129 tcp->Send(getString.C_String(), (unsigned int) getString.GetLength(), serverAddress, false);
130 }
131 phaseTimeout= SLNet::GetTime()+1000;
132 }
133
134 if (connectPhase==CP_WAITING_FOR_CHECKIP_RESPONSE && SLNet::GetTime()>phaseTimeout)
135 {
136 connectPhase = CP_CONNECTING_TO_DYNDNS;
137 tcp->CloseConnection(checkIpAddress);
138 tcp->Connect("members.dyndns.org", 80, false);
139 }
140 else if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE && SLNet::GetTime()>phaseTimeout)
141 {
142 SetCompleted(RC_DYNDNS_TIMEOUT, "DynDNS did not respond");
143 return;
144 }
145
146 Packet *packet = tcp->Receive();
147 if (packet)
148 {
149 if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE)
150 {
151 unsigned int i;
152
153 char *curResult;
154 curResult=strstr((char*) packet->data, "Connection: close");
155 if (curResult!=0)
156 {
157 curResult+=strlen("Connection: close");
158 while (*curResult && ((*curResult=='\r') || (*curResult=='\n') || (*curResult==' ')) )
159 curResult++;
160 for (i=0; i < 13; i++)
161 {
162 if (strncmp(resultTable[i].code, curResult, strlen(resultTable[i].code))==0)
163 {
164 if (resultTable[i].resultCode==RC_SUCCESS)
165 {
166 // Read my external IP into myIPStr
167 // Advance until we hit a number
168 while (*curResult && ((*curResult<'0') || (*curResult>'9')) )
169 curResult++;
170 if (*curResult)
171 {
172 SystemAddress parser;
173 parser.FromString(curResult);
174 parser.ToString(false, myIPStr, static_cast<size_t>(32));
175 }
176 }
177 tcp->DeallocatePacket(packet);
178 SetCompleted(resultTable[i].resultCode, resultTable[i].description);
179 break;
180 }
181 }
182 if (i==13)
183 {
184 tcp->DeallocatePacket(packet);
185 SetCompleted(RC_UNKNOWN_RESULT, "DynDNS returned unknown result");
186 }
187 }
188 else
189 {
190 tcp->DeallocatePacket(packet);
191 SetCompleted(RC_PARSING_FAILURE, "Parsing failure on returned string from DynDNS");
192 }
193
194 return;
195 }
196 else
197 {
198 /*
199 HTTP/1.1 200 OK
200 Content-Type: text/html
201 Server: DynDNS-CheckIP/1.0
202 Connection: close
203 Cache-Control: no-cache
204 Pragma: no-cache
205 Content-Length: 105
206
207 <html><head><title>Current IP Check</title></head><body>Current IP Address: 98.1
208 89.219.22</body></html>
209
210
211 Connection to host lost.
212 */
213
214 char *curResult;
215 curResult=strstr((char*) packet->data, "Current IP Address: ");
216 if (curResult!=0)
217 {
218 curResult+=strlen("Current IP Address: ");
219 SystemAddress myIp;
220 myIp.FromString(curResult);
221 myIp.ToString(false, myIPStr, static_cast<size_t>(32));
222
223 char existingHost[65];
224 existingHost[0]=0;
225 // Resolve DNS we are setting. If equal to current then abort
226 RakNetSocket2::DomainNameToIP(host.C_String(), existingHost);
227 if (strcmp(existingHost, myIPStr)==0)
228 {
229 // DynDNS considers setting the IP to what it is already set abuse
230 tcp->DeallocatePacket(packet);
231 SetCompleted(RC_DNS_ALREADY_SET, "No action needed");
232 return;
233 }
234 }
235
236 tcp->DeallocatePacket(packet);
237 tcp->CloseConnection(packet->systemAddress);
238
239 connectPhase = CP_CONNECTING_TO_DYNDNS;
240 tcp->Connect("members.dyndns.org", 80, false);
241 }
242 }
243
244 if (tcp->HasLostConnection()!=UNASSIGNED_SYSTEM_ADDRESS)
245 {
246 if (connectPhase==CP_WAITING_FOR_DYNDNS_RESPONSE)
247 {
248 SetCompleted(RC_CONNECTION_LOST_WITHOUT_RESPONSE, "Connection lost to DynDNS during GET operation");
249 }
250 }
251 }
252
253
254 #endif // _RAKNET_SUPPORT_DynDNS