rts-socket.c
1 //! @file rts-socket.c
2 //! @author J. Marcel van der Veer
3 //!
4 //! @section Copyright
5 //!
6 //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
7 //! Copyright 2001-2023 J. Marcel van der Veer [algol68g@xs4all.nl].
8 //!
9 //! @section License
10 //!
11 //! This program is free software; you can redistribute it and/or modify it
12 //! under the terms of the GNU General Public License as published by the
13 //! Free Software Foundation; either version 3 of the License, or
14 //! (at your option) any later version.
15 //!
16 //! This program is distributed in the hope that it will be useful, but
17 //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 //! more details. You should have received a copy of the GNU General Public
20 //! License along with this program. If not, see [http://www.gnu.org/licenses/].
21
22 //! @section Synopsis
23 //!
24 //! Low-level socket routines.
25
26 #include "a68g.h"
27 #include "a68g-genie.h"
28 #include "a68g-prelude.h"
29 #include "a68g-transput.h"
30
31 #if defined (BUILD_HTTP)
32
33 #define PROTOCOL "tcp"
34 #define SERVICE "http"
35
36 #define CONTENT_BUFFER_SIZE (64 * KILOBYTE)
37 #define TIMEOUT_INTERVAL 15
38
39 #if defined (BUILD_UNIX)
40
41 //! @brief Send GET request to server and yield answer (TCP/HTTP only).
42
43 void genie_http_content (NODE_T * p)
44 {
45 A68_REF path_string, domain_string, content_string;
46 A68_INT port_number;
47 int socket_id, conn, k;
48 fd_set set;
49 struct timeval a68_timeout;
50 struct servent *service_address;
51 struct hostent *host_address;
52 struct protoent *protocol;
53 struct sockaddr_in socket_address;
54 char buffer[CONTENT_BUFFER_SIZE];
55 errno = 0;
56 // Pop arguments.
57 POP_OBJECT (p, &port_number, A68_INT);
58 CHECK_INIT (p, INITIALISED (&port_number), M_INT);
59 POP_REF (p, &path_string);
60 CHECK_INIT (p, INITIALISED (&path_string), M_STRING);
61 POP_REF (p, &domain_string);
62 CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
63 POP_REF (p, &content_string);
64 CHECK_REF (p, content_string, M_REF_STRING);
65 *DEREF (A68_REF, &content_string) = empty_string (p);
66 // Reset buffers.
67 reset_transput_buffer (DOMAIN_BUFFER);
68 reset_transput_buffer (PATH_BUFFER);
69 reset_transput_buffer (REQUEST_BUFFER);
70 reset_transput_buffer (CONTENT_BUFFER);
71 add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
72 add_a_string_transput_buffer (p, PATH_BUFFER, (BYTE_T *) & path_string);
73 // Make request.
74 add_string_transput_buffer (p, REQUEST_BUFFER, "GET ");
75 add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (PATH_BUFFER));
76 add_string_transput_buffer (p, REQUEST_BUFFER, " HTTP/1.0\n\n");
77 // Connect to host.
78 FILL (&socket_address, 0, (int) sizeof (socket_address));
79 SIN_FAMILY (&socket_address) = AF_INET;
80 service_address = getservbyname (SERVICE, PROTOCOL);
81 if (service_address == NULL) {
82 PUSH_VALUE (p, 1, A68_INT);
83 return;
84 }
85 if (VALUE (&port_number) == 0) {
86 SIN_PORT (&socket_address) = (uint16_t) (S_PORT (service_address));
87 } else {
88 SIN_PORT (&socket_address) = (uint16_t) (htons ((uint16_t) (VALUE (&port_number))));
89 if (SIN_PORT (&socket_address) == 0) {
90 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
91 return;
92 }
93 }
94 host_address = gethostbyname (get_transput_buffer (DOMAIN_BUFFER));
95 if (host_address == NULL) {
96 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
97 return;
98 }
99 COPY (&SIN_ADDR (&socket_address), H_ADDR (host_address), H_LENGTH (host_address));
100 protocol = getprotobyname (PROTOCOL);
101 if (protocol == NULL) {
102 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
103 return;
104 }
105 socket_id = socket (PF_INET, SOCK_STREAM, P_PROTO (protocol));
106 if (socket_id < 0) {
107 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
108 return;
109 }
110 conn = connect (socket_id, (const struct sockaddr *) &socket_address, (socklen_t) SIZE_ALIGNED (socket_address));
111 if (conn < 0) {
112 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
113 ASSERT (close (socket_id) == 0);
114 return;
115 }
116 // Read from host.
117 WRITE (socket_id, get_transput_buffer (REQUEST_BUFFER));
118 if (errno != 0) {
119 PUSH_VALUE (p, errno, A68_INT);
120 ASSERT (close (socket_id) == 0);
121 return;
122 }
123 // Initialise file descriptor set.
124 FD_ZERO (&set);
125 FD_SET (socket_id, &set);
126 // Initialise the a68_timeout data structure.
127 TV_SEC (&a68_timeout) = TIMEOUT_INTERVAL;
128 TV_USEC (&a68_timeout) = 0;
129 // Block until server replies or a68_timeout blows up.
130 switch (select (FD_SETSIZE, &set, NULL, NULL, &a68_timeout)) {
131 case 0:
132 {
133 errno = ETIMEDOUT;
134 PUSH_VALUE (p, errno, A68_INT);
135 ASSERT (close (socket_id) == 0);
136 return;
137 }
138 case -1:
139 {
140 PUSH_VALUE (p, errno, A68_INT);
141 ASSERT (close (socket_id) == 0);
142 return;
143 }
144 case 1:
145 {
146 break;
147 }
148 default:
149 {
150 ABEND (A68_TRUE, ERROR_ACTION, __func__);
151 }
152 }
153 while ((k = (int) io_read (socket_id, &buffer, (CONTENT_BUFFER_SIZE - 1))) > 0) {
154 add_chars_transput_buffer (p, CONTENT_BUFFER, k, buffer);
155 }
156 if (k < 0 || errno != 0) {
157 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
158 ASSERT (close (socket_id) == 0);
159 return;
160 }
161 // Convert string.
162 *DEREF (A68_REF, &content_string) = c_to_a_string (p, get_transput_buffer (CONTENT_BUFFER), get_transput_buffer_index (CONTENT_BUFFER));
163 ASSERT (close (socket_id) == 0);
164 PUSH_VALUE (p, errno, A68_INT);
165 }
166
167 //! @brief Send request to server and yield answer (TCP only).
168
169 void genie_tcp_request (NODE_T * p)
170 {
171 A68_REF path_string, domain_string, content_string;
172 A68_INT port_number;
173 int socket_id, conn, k;
174 fd_set set;
175 struct timeval a68_timeout;
176 struct servent *service_address;
177 struct hostent *host_address;
178 struct protoent *protocol;
179 struct sockaddr_in socket_address;
180 char buffer[CONTENT_BUFFER_SIZE];
181 errno = 0;
182 // Pop arguments.
183 POP_OBJECT (p, &port_number, A68_INT);
184 CHECK_INIT (p, INITIALISED (&port_number), M_INT);
185 POP_REF (p, &path_string);
186 CHECK_INIT (p, INITIALISED (&path_string), M_STRING);
187 POP_REF (p, &domain_string);
188 CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
189 POP_REF (p, &content_string);
190 CHECK_REF (p, content_string, M_REF_STRING);
191 *DEREF (A68_REF, &content_string) = empty_string (p);
192 // Reset buffers.
193 reset_transput_buffer (DOMAIN_BUFFER);
194 reset_transput_buffer (PATH_BUFFER);
195 reset_transput_buffer (REQUEST_BUFFER);
196 reset_transput_buffer (CONTENT_BUFFER);
197 add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
198 add_a_string_transput_buffer (p, PATH_BUFFER, (BYTE_T *) & path_string);
199 // Make request.
200 add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (PATH_BUFFER));
201 // Connect to host.
202 FILL (&socket_address, 0, (int) sizeof (socket_address));
203 SIN_FAMILY (&socket_address) = AF_INET;
204 service_address = getservbyname (SERVICE, PROTOCOL);
205 if (service_address == NULL) {
206 PUSH_VALUE (p, 1, A68_INT);
207 return;
208 }
209 if (VALUE (&port_number) == 0) {
210 SIN_PORT (&socket_address) = (uint16_t) (S_PORT (service_address));
211 } else {
212 SIN_PORT (&socket_address) = (uint16_t) (htons ((uint16_t) (VALUE (&port_number))));
213 if (SIN_PORT (&socket_address) == 0) {
214 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
215 return;
216 }
217 }
218 host_address = gethostbyname (get_transput_buffer (DOMAIN_BUFFER));
219 if (host_address == NULL) {
220 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
221 return;
222 }
223 COPY (&SIN_ADDR (&socket_address), H_ADDR (host_address), H_LENGTH (host_address));
224 protocol = getprotobyname (PROTOCOL);
225 if (protocol == NULL) {
226 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
227 return;
228 }
229 socket_id = socket (PF_INET, SOCK_STREAM, P_PROTO (protocol));
230 if (socket_id < 0) {
231 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
232 return;
233 }
234 conn = connect (socket_id, (const struct sockaddr *) &socket_address, (socklen_t) SIZE_ALIGNED (socket_address));
235 if (conn < 0) {
236 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
237 ASSERT (close (socket_id) == 0);
238 return;
239 }
240 // Read from host.
241 WRITE (socket_id, get_transput_buffer (REQUEST_BUFFER));
242 if (errno != 0) {
243 PUSH_VALUE (p, errno, A68_INT);
244 ASSERT (close (socket_id) == 0);
245 return;
246 }
247 // Initialise file descriptor set.
248 FD_ZERO (&set);
249 FD_SET (socket_id, &set);
250 // Initialise the a68_timeout data structure.
251 TV_SEC (&a68_timeout) = TIMEOUT_INTERVAL;
252 TV_USEC (&a68_timeout) = 0;
253 // Block until server replies or a68_timeout blows up.
254 switch (select (FD_SETSIZE, &set, NULL, NULL, &a68_timeout)) {
255 case 0:
256 {
257 errno = ETIMEDOUT;
258 PUSH_VALUE (p, errno, A68_INT);
259 ASSERT (close (socket_id) == 0);
260 return;
261 }
262 case -1:
263 {
264 PUSH_VALUE (p, errno, A68_INT);
265 ASSERT (close (socket_id) == 0);
266 return;
267 }
268 case 1:
269 {
270 break;
271 }
272 default:
273 {
274 ABEND (A68_TRUE, ERROR_ACTION, __func__);
275 }
276 }
277 while ((k = (int) io_read (socket_id, &buffer, (CONTENT_BUFFER_SIZE - 1))) > 0) {
278 add_chars_transput_buffer (p, CONTENT_BUFFER, k, buffer);
279 }
280 if (k < 0 || errno != 0) {
281 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
282 ASSERT (close (socket_id) == 0);
283 return;
284 }
285 // Convert string.
286 *DEREF (A68_REF, &content_string) = c_to_a_string (p, get_transput_buffer (CONTENT_BUFFER), get_transput_buffer_index (CONTENT_BUFFER));
287 ASSERT (close (socket_id) == 0);
288 PUSH_VALUE (p, errno, A68_INT);
289 }
290
291 #endif
292
293 #if defined (BUILD_WIN32)
294
295 #if defined (HAVE_WINSOCK_H)
296 #include <winsock.h>
297 #endif
298 typedef int socklen_t;
299
300 //! @brief Send GET request to server and yield answer (TCP/HTTP only).
301
302 void genie_http_content (NODE_T * p)
303 {
304 WSADATA wsa_data;
305 A68_REF path_string, domain_string, content_string;
306 A68_INT port_number;
307 int socket_id, conn, k, rc, len, sent;
308 struct servent *service_address;
309 struct hostent *host_address;
310 struct protoent *protocol;
311 struct sockaddr_in socket_address;
312 char buffer[CONTENT_BUFFER_SIZE];
313 char *str;
314 errno = 0;
315 // Pop arguments.
316 POP_OBJECT (p, &port_number, A68_INT);
317 CHECK_INIT (p, INITIALISED (&port_number), M_INT);
318 POP_REF (p, &path_string);
319 CHECK_INIT (p, INITIALISED (&path_string), M_STRING);
320 POP_REF (p, &domain_string);
321 CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
322 POP_REF (p, &content_string);
323 CHECK_REF (p, content_string, M_REF_STRING);
324 *DEREF (A68_REF, &content_string) = empty_string (p);
325 // Reset buffers.
326 reset_transput_buffer (DOMAIN_BUFFER);
327 reset_transput_buffer (PATH_BUFFER);
328 reset_transput_buffer (REQUEST_BUFFER);
329 reset_transput_buffer (CONTENT_BUFFER);
330 add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
331 add_a_string_transput_buffer (p, PATH_BUFFER, (BYTE_T *) & path_string);
332 // Make request.
333 add_string_transput_buffer (p, REQUEST_BUFFER, "GET ");
334 add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (PATH_BUFFER));
335 add_string_transput_buffer (p, REQUEST_BUFFER, " HTTP/1.0\n\n");
336 // Connect to host.
337 if (WSAStartup (MAKEWORD (1, 1), &wsa_data) != NO_ERROR) {
338 PUSH_VALUE (p, 1, A68_INT);
339 return;
340 }
341 FILL (&socket_address, 0, (int) sizeof (socket_address));
342 SIN_FAMILY (&socket_address) = AF_INET;
343 service_address = getservbyname (SERVICE, PROTOCOL);
344 if (service_address == NULL) {
345 PUSH_VALUE (p, 1, A68_INT);
346 WSACleanup ();
347 return;
348 }
349 if (VALUE (&port_number) == 0) {
350 SIN_PORT (&socket_address) = (uint16_t) (S_PORT (service_address));
351 } else {
352 SIN_PORT (&socket_address) = (uint16_t) (htons ((uint16_t) (VALUE (&port_number))));
353 if (SIN_PORT (&socket_address) == 0) {
354 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
355 WSACleanup ();
356 return;
357 }
358 }
359 host_address = gethostbyname (get_transput_buffer (DOMAIN_BUFFER));
360 if (host_address == NULL) {
361 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
362 WSACleanup ();
363 return;
364 }
365 COPY (&SIN_ADDR (&socket_address), H_ADDR (host_address), H_LENGTH (host_address));
366 protocol = getprotobyname (PROTOCOL);
367 if (protocol == NULL) {
368 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
369 WSACleanup ();
370 return;
371 }
372 socket_id = socket (PF_INET, SOCK_STREAM, P_PROTO (protocol));
373 if (socket_id < 0) {
374 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
375 WSACleanup ();
376 return;
377 }
378 conn = connect (socket_id, (const struct sockaddr *) &socket_address, (socklen_t) SIZE_ALIGNED (socket_address));
379 if (conn < 0) {
380 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
381 WSACleanup ();
382 return;
383 }
384 // Send request to host.
385 str = get_transput_buffer (REQUEST_BUFFER);
386 len = (int) strlen (str);
387 sent = 0;
388 while (sent < len) {
389 rc = send (socket_id, &str[sent], len - sent, 0);
390 if (rc == SOCKET_ERROR) {
391 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
392 WSACleanup ();
393 }
394 sent += rc;
395 }
396 // Receive data from host.
397 while ((k = (int) recv (socket_id, (char *) &buffer, (CONTENT_BUFFER_SIZE - 1), 0)) > 0) {
398 add_chars_transput_buffer (p, CONTENT_BUFFER, k, buffer);
399 }
400 if (k < 0 || errno != 0) {
401 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
402 WSACleanup ();
403 return;
404 }
405 // Convert string.
406 *DEREF (A68_REF, &content_string) = c_to_a_string (p, get_transput_buffer (CONTENT_BUFFER), get_transput_buffer_index (CONTENT_BUFFER));
407 if (k != 0) {
408 // Not gracefully closed by recv ().
409 ASSERT (close (socket_id) == 0);
410 }
411 PUSH_VALUE (p, errno, A68_INT);
412 WSACleanup ();
413 }
414
415 //! @brief Send request to server and yield answer (TCP only).
416
417 void genie_tcp_request (NODE_T * p)
418 {
419 WSADATA wsa_data;
420 A68_REF path_string, domain_string, content_string;
421 A68_INT port_number;
422 int socket_id, conn, k, rc, len, sent;
423 struct servent *service_address;
424 struct hostent *host_address;
425 struct protoent *protocol;
426 struct sockaddr_in socket_address;
427 char buffer[CONTENT_BUFFER_SIZE];
428 char *str;
429 errno = 0;
430 // Pop arguments.
431 POP_OBJECT (p, &port_number, A68_INT);
432 CHECK_INIT (p, INITIALISED (&port_number), M_INT);
433 POP_REF (p, &path_string);
434 CHECK_INIT (p, INITIALISED (&path_string), M_STRING);
435 POP_REF (p, &domain_string);
436 CHECK_INIT (p, INITIALISED (&domain_string), M_STRING);
437 POP_REF (p, &content_string);
438 CHECK_REF (p, content_string, M_REF_STRING);
439 *DEREF (A68_REF, &content_string) = empty_string (p);
440 // Reset buffers.
441 reset_transput_buffer (DOMAIN_BUFFER);
442 reset_transput_buffer (PATH_BUFFER);
443 reset_transput_buffer (REQUEST_BUFFER);
444 reset_transput_buffer (CONTENT_BUFFER);
445 add_a_string_transput_buffer (p, DOMAIN_BUFFER, (BYTE_T *) & domain_string);
446 add_a_string_transput_buffer (p, PATH_BUFFER, (BYTE_T *) & path_string);
447 // Make request.
448 add_string_transput_buffer (p, REQUEST_BUFFER, get_transput_buffer (PATH_BUFFER));
449 // Connect to host.
450 if (WSAStartup (MAKEWORD (1, 1), &wsa_data) != NO_ERROR) {
451 PUSH_VALUE (p, 1, A68_INT);
452 return;
453 }
454 FILL (&socket_address, 0, (int) sizeof (socket_address));
455 SIN_FAMILY (&socket_address) = AF_INET;
456 service_address = getservbyname (SERVICE, PROTOCOL);
457 if (service_address == NULL) {
458 PUSH_VALUE (p, 1, A68_INT);
459 WSACleanup ();
460 return;
461 }
462 if (VALUE (&port_number) == 0) {
463 SIN_PORT (&socket_address) = (uint16_t) (S_PORT (service_address));
464 } else {
465 SIN_PORT (&socket_address) = (uint16_t) (htons ((uint16_t) (VALUE (&port_number))));
466 if (SIN_PORT (&socket_address) == 0) {
467 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
468 WSACleanup ();
469 return;
470 }
471 }
472 host_address = gethostbyname (get_transput_buffer (DOMAIN_BUFFER));
473 if (host_address == NULL) {
474 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
475 WSACleanup ();
476 return;
477 }
478 COPY (&SIN_ADDR (&socket_address), H_ADDR (host_address), H_LENGTH (host_address));
479 protocol = getprotobyname (PROTOCOL);
480 if (protocol == NULL) {
481 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
482 WSACleanup ();
483 return;
484 }
485 socket_id = socket (PF_INET, SOCK_STREAM, P_PROTO (protocol));
486 if (socket_id < 0) {
487 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
488 WSACleanup ();
489 return;
490 }
491 conn = connect (socket_id, (const struct sockaddr *) &socket_address, (socklen_t) SIZE_ALIGNED (socket_address));
492 if (conn < 0) {
493 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
494 WSACleanup ();
495 return;
496 }
497 // Send request to host.
498 str = get_transput_buffer (REQUEST_BUFFER);
499 len = (int) strlen (str);
500 sent = 0;
501 while (sent < len) {
502 rc = send (socket_id, &str[sent], len - sent, 0);
503 if (rc == SOCKET_ERROR) {
504 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
505 WSACleanup ();
506 }
507 sent += rc;
508 }
509 // Receive data from host.
510 while ((k = (int) recv (socket_id, (char *) &buffer, (CONTENT_BUFFER_SIZE - 1), 0)) > 0) {
511 add_chars_transput_buffer (p, CONTENT_BUFFER, k, buffer);
512 }
513 if (k < 0 || errno != 0) {
514 PUSH_VALUE (p, (errno == 0 ? 1 : errno), A68_INT);
515 WSACleanup ();
516 return;
517 }
518 // Convert string.
519 *DEREF (A68_REF, &content_string) = c_to_a_string (p, get_transput_buffer (CONTENT_BUFFER), get_transput_buffer_index (CONTENT_BUFFER));
520 if (k != 0) {
521 // Not gracefully closed by recv ().
522 ASSERT (close (socket_id) == 0);
523 }
524 PUSH_VALUE (p, errno, A68_INT);
525 WSACleanup ();
526 }
527
528 #endif
529
530 #endif