AmendHub

Download

cyberslak

/

lightsout

/

net.c

 

(View History)

cyberslak   code cleanup Latest amendment: 23 on 2025-03-17

1 // SPDX-License-Identifier: MIT
2
3 #include <Threads.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include "net.h"
7 #include "util.h"
8 #include "dbuf.h"
9
10 #define OT_CHECK(x) \
11 { \
12 OTResult _m_res = (x); \
13 if (_m_res < 0) \
14 die("OT Error at %s:%d: %ld", \
15 __FILE__, __LINE__, _m_res); \
16 } \
17
18 #define kConnectionTimeoutSecs 2
19
20 static bool gOTInited = false;
21 static InetSvcRef inet_svcs;
22
23 static OTResult SetFourByteOption(EndpointRef ep,
24 OTXTILevel level, OTXTIName name, UInt32 value);
25
26 /* Look up the IPv4 address of a given host using DNS. */
27 short net_dns_lookup(const char* hostname, InetHost *host)
28 {
29 InetHostInfo hi;
30 memset(&hi, 0, sizeof(InetHostInfo));
31
32 if (OTInetStringToAddress(inet_svcs, (char*)hostname, &hi) < 0)
33 return -1;
34
35 *host = hi.addrs[0];
36 return 0;
37 }
38
39 void net_init()
40 {
41 OSStatus err = noErr;
42 OT_CHECK(InitOpenTransport());
43
44 // docs say you should be able to use
45 // nil as a first parameter, but at least
46 // OT 1.1 does _not_ like that.
47 inet_svcs = OTOpenInternetServices(
48 kDefaultInternetServicesPath, 0, &err);
49
50 OT_CHECK(err);
51 gOTInited = true;
52 }
53
54 EndpointRef net_create_endpoint()
55 {
56 EndpointRef ep;
57 OSStatus err = noErr;
58
59 ep = OTOpenEndpoint(
60 OTCreateConfiguration("tcp"),
61 0, NULL, &err);
62
63 OT_CHECK(err);
64 OT_CHECK(OTSetSynchronous(ep));
65 OT_CHECK(OTSetBlocking(ep));
66 OT_CHECK(OTBind(ep, nil, nil));
67 OT_CHECK(OTSetNonBlocking(ep));
68
69 /* Set reasonable connection timeout (2s) */
70
71 OT_CHECK(SetFourByteOption(ep, INET_TCP, TCP_CONN_ABORT_THRESHOLD,
72 kConnectionTimeoutSecs * 1000));
73 OT_CHECK(SetFourByteOption(ep, INET_TCP, TCP_CONN_NOTIFY_THRESHOLD,
74 (kConnectionTimeoutSecs * 1000 / 2)));
75
76 return ep;
77 }
78
79 short net_wait_readable(EndpointRef ep)
80 {
81 OTResult res;
82
83 while (true)
84 {
85 res = OTLook(ep);
86 switch (res)
87 {
88 case T_DATA:
89 return 0; // readable
90 case T_DISCONNECT:
91 warn("net_wait_readable: unorderly disconnect");
92 OT_CHECK(OTRcvDisconnect(ep, nil));
93 return -1;
94 case T_ORDREL:
95 OTRcvOrderlyDisconnect(ep);
96 OTSndOrderlyDisconnect(ep);
97 return -1;
98 case kOTNoError:
99 // nothing doing
100 break;
101 default:
102 warn("unhandled OTLook result: %x (%d)", res, res);
103 break;
104 }
105 YieldToAnyThread();
106 }
107 }
108
109 EndpointRef net_connect(const char* hostname, short port)
110 {
111 EndpointRef ep = net_create_endpoint();
112 TCall connectCall;
113 InetAddress addr;
114 InetHost host;
115
116 if (net_dns_lookup(hostname, &host) < 0)
117 goto fail;
118
119 OTInitInetAddress(&addr, port, host);
120
121 OTMemzero(&connectCall, sizeof(TCall));
122
123 connectCall.addr.buf = (unsigned char*)&addr;
124 connectCall.addr.len = sizeof(InetAddress);
125
126 if (OTConnect(ep, &connectCall, nil) < 0)
127 goto fail;
128
129 return ep;
130
131 fail:
132 OTCloseProvider(ep);
133 return NULL;
134 }
135
136 EndpointRef
137 net_socks_connect(
138 const char* proxy_host, short proxy_port,
139 const char* host, short port)
140 {
141 EndpointRef ep;
142 OTResult res;
143 char hello[] = "\x05\x01\x00"; // SOCKSv5, 1 auth method, 00 = no auth
144 long host_len = strlen(host);
145 unsigned char req[384];
146 unsigned char respBuf[384];
147 short idx = 0;
148 OTFlags flags;
149
150 ep = net_connect(proxy_host, proxy_port);
151 if (!ep)
152 return NULL;
153
154 if (host_len > 253)
155 die("invalid host %s; too long!", host);
156
157 req[idx++] = 5; // SOCKSv5
158 req[idx++] = 1; // CMD 1: CONNECT
159 req[idx++] = 0; // reserved
160 req[idx++] = 3; // Address type: DNS
161
162 req[idx++] = (unsigned char)host_len; // Length of DNS address
163
164 memcpy(&req[idx], host, host_len);
165 idx += host_len;
166
167 // 68K and PPC are big-endian, so port
168 // is already in network byte order
169 *((short*)&req[idx]) = port;
170 idx += 2;
171
172 res = OTSnd(ep, hello, sizeof(hello), 0);
173 OT_CHECK(res);
174
175 OTSetBlocking(ep);
176 res = OTRcv(ep, respBuf, 2, &flags);
177
178 if (respBuf[0] != 5 || respBuf[1] != 0)
179 die("invalid response: %d %d", respBuf[0], respBuf[1]);
180
181 res = OTSnd(ep, req, idx, 0);
182 OT_CHECK(res);
183
184 res = OTRcv(ep, respBuf, 4, &flags);
185 if (respBuf[0] != 5 || respBuf[1] != 0 || respBuf[3] != 1)
186 die("invalid response: %d %d %d", respBuf[0], respBuf[1], respBuf[3]);
187
188 res = OTRcv(ep, respBuf + 4, 6, &flags);
189
190 OTSetNonBlocking(ep);
191
192 return ep;
193 }
194
195 static void net_recv_response(EndpointRef ep, struct dbuf* db)
196 {
197 OTResult res;
198 OTFlags flags;
199 char tmpBuf[256];
200
201 while (true)
202 {
203 short readable = net_wait_readable(ep);
204 if (readable == -1) break;
205 else if (readable == 0)
206 {
207 res = OTRcv(ep, tmpBuf, sizeof(tmpBuf), &flags);
208 if (res < 0)
209 die("readable socket returned error on read? %ld", res);
210 db_extend(db, (u8*)tmpBuf, res);
211 }
212 }
213
214 // null-terminate response
215 db_extend(db, (u8*)"\0", 1);
216 }
217
218 struct dbuf net_post(EndpointRef ep, const char* path, const char* token, const char* content)
219 {
220 size_t content_length = strlen(content);
221 DB_INIT(db, 8192);
222 OTResult res;
223
224 db_printf(&db, "POST %s HTTP/1.1\r\n"
225 "Authorization: Bearer %s\r\n"
226 "Host: homeassistant.lan\r\n"
227 "Connection: close\r\n"
228 "Content-Type: application/json\r\n"
229 "Content-Length: %lu\r\n"
230 "\r\n%s\r\n", path, token, content_length, content);
231
232 res = OTSnd(ep, (void*)&db.buf[0], db.len, 0);
233 OT_CHECK(res);
234
235 db_clear(&db);
236
237 net_recv_response(ep, &db);
238 return db;
239 }
240
241 struct dbuf net_get(EndpointRef ep, const char* path, const char* token)
242 {
243 OTResult res;
244 DB_INIT(db, 8192);
245
246 db_printf(&db, "GET %s HTTP/1.1\r\n"
247 "Authorization: Bearer %s\r\n"
248 "Host: homeassistant.lan\r\n"
249 "Connection: close\r\n"
250 "\r\n", path, token);
251
252 res = OTSnd(ep, (void*)&db.buf[0], db.len, 0);
253 OT_CHECK(res);
254
255 db_clear(&db);
256
257 net_recv_response(ep, &db);
258 return db;
259 }
260
261 /* Get a pointer to the content in an HTTP response. */
262 char* net_http_response_content(const char* response)
263 {
264 char* s = strstr(response, "\r\n\r\n");
265 if (!s)
266 return NULL;
267 return s + 4;
268 }
269
270 short net_test_socks()
271 {
272 EndpointRef ep = net_socks_connect("192.168.1.187", 1080, "httpbin.org", 443);
273 struct dbuf respBuf;
274
275 if (!ep)
276 return -1;
277
278 respBuf = net_get(ep, "/uuid", "");
279
280 info("%s", respBuf.buf);
281 db_destroy(&respBuf);
282 OTCloseProvider(ep);
283
284 return 0;
285 }
286
287 /* Taken from the OT sample code here:
288 * https://lists.apple.com/archives/macnetworkprog/2004/Jan/msg00145.html
289 */
290 static OTResult SetFourByteOption(EndpointRef ep,
291 OTXTILevel level, OTXTIName name, UInt32 value)
292 // level and name must denote a four byte option that is
293 // appropriate for the endpoint ep. This routine sets the
294 // option to value. ep is assumed to be in synchronous
295 // mode.
296 //
297 // If all goes well, the result is noErr. If an error
298 // occurs, the result is negative. If the option could not
299 // be negotiated, a positive result being one of (T_FAILURE,
300 // T_PARTSUCCESS, T_READONLY, T_NOTSUPPORT) is returned
301 {
302 OTResult err;
303 TOption option;
304 TOptMgmt request;
305 TOptMgmt result;
306 // Set up the option buffer to reflect the specific option
307 // and value we want to set. We use a TOption structure
308 // to represent the option buffer. TOption is specifically
309 // defined to allow easy construction of 4 byte options.
310 // If you want to negotiate different size options, or
311 // multiple options in a single call, then constructing
312 // the option buffer is a little trickier
313 option.len = kOTFourByteOptionSize;
314 option.level = level;
315 option.name = name;
316 option.status = 0;
317 option.value[0] = value;
318
319 // Set up the request for OTOptionManagement to point
320 // to the option buffer we just filled out, and tell
321 // it that we want to negotiate (ie set) the option.
322 request.opt.buf = (UInt8 *) &option;
323 request.opt.len = sizeof(option);
324 request.flags = T_NEGOTIATE;
325
326 // Set up the reply for OTOptionManagement. This is where
327 // OTOptionManagement puts the result of the negotiation.
328 result.opt.buf = (UInt8 *) &option;
329 result.opt.maxlen = sizeof(option);
330
331 // Call OTOptionManagement and then check that the value
332 // was negotiated successfully. Any value other than
333 // T_SUCCESS is reported via the error result.
334 err = OTOptionManagement(ep, &request, &result);
335
336 if (err == noErr) {
337 if (option.status != T_SUCCESS) {
338 err = option.status;
339 }
340 }
341
342 return (err);
343 }
344
345 void net_fini()
346 {
347 if (gOTInited)
348 {
349 OTCloseProvider(inet_svcs);
350 CloseOpenTransport();
351 }
352 }