AmendHub

Download

cyberslak

/

lightsout

/

net.c

 

(View History)

cyberslak   Properly check for Thread Manager before using it Latest amendment: 25 on 2025-04-21

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
106 if (have_thread_manager())
107 YieldToAnyThread();
108 }
109 }
110
111 EndpointRef net_connect(const char* hostname, short port)
112 {
113 EndpointRef ep = net_create_endpoint();
114 TCall connectCall;
115 InetAddress addr;
116 InetHost host;
117
118 if (net_dns_lookup(hostname, &host) < 0)
119 goto fail;
120
121 OTInitInetAddress(&addr, port, host);
122
123 OTMemzero(&connectCall, sizeof(TCall));
124
125 connectCall.addr.buf = (unsigned char*)&addr;
126 connectCall.addr.len = sizeof(InetAddress);
127
128 if (OTConnect(ep, &connectCall, nil) < 0)
129 goto fail;
130
131 return ep;
132
133 fail:
134 OTCloseProvider(ep);
135 return NULL;
136 }
137
138 EndpointRef
139 net_socks_connect(
140 const char* proxy_host, short proxy_port,
141 const char* host, short port)
142 {
143 EndpointRef ep;
144 OTResult res;
145 char hello[] = "\x05\x01\x00"; // SOCKSv5, 1 auth method, 00 = no auth
146 long host_len = strlen(host);
147 unsigned char req[384];
148 unsigned char respBuf[384];
149 short idx = 0;
150 OTFlags flags;
151
152 ep = net_connect(proxy_host, proxy_port);
153 if (!ep)
154 return NULL;
155
156 if (host_len > 253)
157 die("invalid host %s; too long!", host);
158
159 req[idx++] = 5; // SOCKSv5
160 req[idx++] = 1; // CMD 1: CONNECT
161 req[idx++] = 0; // reserved
162 req[idx++] = 3; // Address type: DNS
163
164 req[idx++] = (unsigned char)host_len; // Length of DNS address
165
166 memcpy(&req[idx], host, host_len);
167 idx += host_len;
168
169 // 68K and PPC are big-endian, so port
170 // is already in network byte order
171 *((short*)&req[idx]) = port;
172 idx += 2;
173
174 res = OTSnd(ep, hello, sizeof(hello), 0);
175 OT_CHECK(res);
176
177 OTSetBlocking(ep);
178 res = OTRcv(ep, respBuf, 2, &flags);
179
180 if (respBuf[0] != 5 || respBuf[1] != 0)
181 die("invalid response: %d %d", respBuf[0], respBuf[1]);
182
183 res = OTSnd(ep, req, idx, 0);
184 OT_CHECK(res);
185
186 res = OTRcv(ep, respBuf, 4, &flags);
187 if (respBuf[0] != 5 || respBuf[1] != 0 || respBuf[3] != 1)
188 die("invalid response: %d %d %d", respBuf[0], respBuf[1], respBuf[3]);
189
190 res = OTRcv(ep, respBuf + 4, 6, &flags);
191
192 OTSetNonBlocking(ep);
193
194 return ep;
195 }
196
197 static void net_recv_response(EndpointRef ep, struct dbuf* db)
198 {
199 OTResult res;
200 OTFlags flags;
201 char tmpBuf[256];
202
203 while (true)
204 {
205 short readable = net_wait_readable(ep);
206 if (readable == -1) break;
207 else if (readable == 0)
208 {
209 res = OTRcv(ep, tmpBuf, sizeof(tmpBuf), &flags);
210 if (res < 0)
211 die("readable socket returned error on read? %ld", res);
212 db_extend(db, (u8*)tmpBuf, res);
213 }
214 }
215
216 // null-terminate response
217 db_extend(db, (u8*)"\0", 1);
218 }
219
220 struct dbuf net_post(EndpointRef ep, const char* path, const char* token, const char* content)
221 {
222 size_t content_length = strlen(content);
223 DB_INIT(db, 8192);
224 OTResult res;
225
226 db_printf(&db, "POST %s HTTP/1.1\r\n"
227 "Authorization: Bearer %s\r\n"
228 "Host: homeassistant.lan\r\n"
229 "Connection: close\r\n"
230 "Content-Type: application/json\r\n"
231 "Content-Length: %lu\r\n"
232 "\r\n%s\r\n", path, token, content_length, content);
233
234 res = OTSnd(ep, (void*)&db.buf[0], db.len, 0);
235 OT_CHECK(res);
236
237 db_clear(&db);
238
239 net_recv_response(ep, &db);
240 return db;
241 }
242
243 struct dbuf net_get(EndpointRef ep, const char* path, const char* token)
244 {
245 OTResult res;
246 DB_INIT(db, 8192);
247
248 db_printf(&db, "GET %s HTTP/1.1\r\n"
249 "Authorization: Bearer %s\r\n"
250 "Host: homeassistant.lan\r\n"
251 "Connection: close\r\n"
252 "\r\n", path, token);
253
254 res = OTSnd(ep, (void*)&db.buf[0], db.len, 0);
255 OT_CHECK(res);
256
257 db_clear(&db);
258
259 net_recv_response(ep, &db);
260 return db;
261 }
262
263 /* Get a pointer to the content in an HTTP response. */
264 char* net_http_response_content(const char* response)
265 {
266 char* s = strstr(response, "\r\n\r\n");
267 if (!s)
268 return NULL;
269 return s + 4;
270 }
271
272 short net_test_socks()
273 {
274 EndpointRef ep = net_socks_connect("192.168.1.187", 1080, "httpbin.org", 443);
275 struct dbuf respBuf;
276
277 if (!ep)
278 return -1;
279
280 respBuf = net_get(ep, "/uuid", "");
281
282 info("%s", respBuf.buf);
283 db_destroy(&respBuf);
284 OTCloseProvider(ep);
285
286 return 0;
287 }
288
289 /* Taken from the OT sample code here:
290 * https://lists.apple.com/archives/macnetworkprog/2004/Jan/msg00145.html
291 */
292 static OTResult SetFourByteOption(EndpointRef ep,
293 OTXTILevel level, OTXTIName name, UInt32 value)
294 // level and name must denote a four byte option that is
295 // appropriate for the endpoint ep. This routine sets the
296 // option to value. ep is assumed to be in synchronous
297 // mode.
298 //
299 // If all goes well, the result is noErr. If an error
300 // occurs, the result is negative. If the option could not
301 // be negotiated, a positive result being one of (T_FAILURE,
302 // T_PARTSUCCESS, T_READONLY, T_NOTSUPPORT) is returned
303 {
304 OTResult err;
305 TOption option;
306 TOptMgmt request;
307 TOptMgmt result;
308 // Set up the option buffer to reflect the specific option
309 // and value we want to set. We use a TOption structure
310 // to represent the option buffer. TOption is specifically
311 // defined to allow easy construction of 4 byte options.
312 // If you want to negotiate different size options, or
313 // multiple options in a single call, then constructing
314 // the option buffer is a little trickier
315 option.len = kOTFourByteOptionSize;
316 option.level = level;
317 option.name = name;
318 option.status = 0;
319 option.value[0] = value;
320
321 // Set up the request for OTOptionManagement to point
322 // to the option buffer we just filled out, and tell
323 // it that we want to negotiate (ie set) the option.
324 request.opt.buf = (UInt8 *) &option;
325 request.opt.len = sizeof(option);
326 request.flags = T_NEGOTIATE;
327
328 // Set up the reply for OTOptionManagement. This is where
329 // OTOptionManagement puts the result of the negotiation.
330 result.opt.buf = (UInt8 *) &option;
331 result.opt.maxlen = sizeof(option);
332
333 // Call OTOptionManagement and then check that the value
334 // was negotiated successfully. Any value other than
335 // T_SUCCESS is reported via the error result.
336 err = OTOptionManagement(ep, &request, &result);
337
338 if (err == noErr) {
339 if (option.status != T_SUCCESS) {
340 err = option.status;
341 }
342 }
343
344 return (err);
345 }
346
347 void net_fini()
348 {
349 if (gOTInited)
350 {
351 OTCloseProvider(inet_svcs);
352 CloseOpenTransport();
353 }
354 }