Annotation of freem/src/io_socket.c, revision 1.3
1.1 snw 1: /*
1.3 ! snw 2: * $Id$
1.1 snw 3: * socket i/o support
4: *
5: *
1.2 snw 6: * Author: Serena Willis <snw@coherent-logic.com>
1.1 snw 7: * Copyright (C) 1998 MUG Deutschland
1.3 ! snw 8: * Copyright (C) 2020, 2025 Coherent Logic Development LLC
1.1 snw 9: *
10: *
11: * This file is part of FreeM.
12: *
13: * FreeM is free software: you can redistribute it and/or modify
14: * it under the terms of the GNU Affero Public License as published by
15: * the Free Software Foundation, either version 3 of the License, or
16: * (at your option) any later version.
17: *
18: * FreeM is distributed in the hope that it will be useful,
19: * but WITHOUT ANY WARRANTY; without even the implied warranty of
20: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21: * GNU Affero Public License for more details.
22: *
23: * You should have received a copy of the GNU Affero Public License
24: * along with FreeM. If not, see <https://www.gnu.org/licenses/>.
25: *
1.3 ! snw 26: * $Log$
! 27: *
! 28: * SPDX-FileCopyrightText: (C) 2025 Coherent Logic Development LLC
! 29: * SPDX-License-Identifier: AGPL-3.0-or-later
1.1 snw 30: **/
31:
32: #include <stdio.h>
33: #include <stdlib.h>
34: #include <string.h>
35: #include <ctype.h>
36: #include <signal.h>
37: #include <setjmp.h>
38:
39: #if !defined(MSDOS)
40: # include <sys/socket.h>
41: # include <arpa/inet.h>
42: # include <netdb.h>
43: #endif
44:
45: #if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(_SCO_DS) && !defined(MSDOS)
46: # include <netinet/in.h>
47: #endif
48:
49: #if !defined(__OpenBSD__) && !defined(__FreeBSD__)
50: # include <sys/timeb.h>
51: #endif
52:
53: #if defined(__NetBSD__) || defined(__FreeBSD__) || defined(_SCO_DS)
54: # define USE_SYS_TIME_H
55: #endif
56:
57: #if defined(_SCO_DS)
58: # define SHUT_RDWR 2
59: #endif
60:
61: #ifdef USE_SYS_TIME_H
62: #include <sys/time.h>
63: #endif
64:
65: #if defined(__APPLE__)
66: # include <sys/select.h>
67: #endif
68:
69: #include "mpsdef.h"
70: #include "mref.h"
71: #include "io_socket.h"
72:
73: io_socket *io_sockets[MAXSCK];
74:
75: /* channel: channel number
76: addr_string: server:port[:family:[udp|tcp]]*/
77: short msck_open (int channel, char *addr_string)
78: {
79: #if !defined(MSDOS)
80: char *addr = "";
81: char *port = "";
82: char *family = "";
83: char *typ = "";
84:
85: char *finaddr;
86:
87: struct hostent *he;
88: struct in_addr **addr_list;
89:
90: int pt = 0;
91: int fm = 0;
92: int tp = 0;
93:
94: short ct = 0;
95: register int j = 0;
96:
97: short i = channel + FIRSTSCK; /* get index into io_sockets[] array */
98:
99: finaddr = (char *) malloc (STRLEN * sizeof (char));
100: NULLPTRCHK(finaddr,"msck_open");
101:
102:
103: merr_clear_iochan_err (channel);
104:
105: for (j = 0; j < strlen (addr_string); j++) {
106: if (addr_string[j] == ':') ct++;
107: }
108:
109: if (ct < 1 || ct > 3) {
110: merr_raise (ARGLIST);
111: merr_set_iochan_err (channel, ARGLIST, "invalid OPEN parameters");
112: return -1;
113: }
114:
115: addr = strtok (addr_string, ":");
116: port = strtok (NULL, ":");
117:
118: if (ct == 1) {
119:
120: family = (char *) malloc (STRLEN * sizeof (char));
121: NULLPTRCHK(family,"msck_open");
122:
123: typ = (char *) malloc (STRLEN * sizeof (char));
124: NULLPTRCHK(typ,"msck_open");
125:
126: strcpy (family, "IPV4");
127: strcpy (typ, "TCP");
128: }
129:
130: if (ct == 2) {
131: family = strtok (NULL, ":");
132:
133: typ = (char *) malloc (STRLEN * sizeof (char));
134: NULLPTRCHK(typ,"msck_open");
135:
136: strcpy (typ, "TCP");
137: }
138:
139: if (ct == 3) {
140: family = strtok (NULL, ":");
141: typ = strtok (NULL, ":");
142: }
143:
144: if (strcmp (family, "IPV4") == 0) {
145: fm = AF_INET;
146: }
147: #if !defined(_SCO_DS) && defined(AF_INET6)
148: else if (strcmp (family, "IPV6") == 0) {
149: fm = AF_INET6;
150: }
151: #endif
152: else {
153: merr_raise (SCKIFAM);
154: merr_set_iochan_err (channel, SCKIFAM, "invalid address family");
155: return 0;
156: }
157:
158: if (strcmp (typ, "TCP") == 0) {
159: tp = SOCK_STREAM;
160: }
161: else if (strcmp (typ, "UDP") == 0) {
162: tp = SOCK_DGRAM;
163: }
164: else {
165: merr_raise (SCKITYP);
166: return 0;
167: }
168:
169: io_sockets[i] = (io_socket *) malloc (sizeof (io_socket));
170: NULLPTRCHK(io_sockets[i],"msck_open");
171:
172: io_sockets[i]->io_channel = channel;
173: io_sockets[i]->typ = tp;
174: io_sockets[i]->sck = socket (fm, tp, 0);
175: io_sockets[i]->srv.sin_family = fm;
176:
177: pt = atoi (port);
178:
179: if (pt < 0 || pt > 65535) {
180: merr_raise (SCKIPRT);
181: merr_set_iochan_err (channel, SCKIPRT, "invalid port number");
182: return 0;
183: }
184:
185: io_sockets[i]->srv.sin_port = htons (pt);
186:
187: #if !defined(INADDR_NONE)
188: # define INADDR_NONE -1
189: #endif
190:
191: if (inet_addr (addr) == INADDR_NONE) {
192:
193: /* addr is not a valid IP. we need to do a nameserver lookup. */
194:
195: if ((he = gethostbyname (addr)) == NULL) {
196: merr_raise (NAMERES);
197: merr_set_iochan_err (channel, NAMERES, "name resolution failure");
198: return 0;
199: }
200:
201: addr_list = (struct in_addr **) he->h_addr_list;
202:
203: strcpy (finaddr, inet_ntoa (*addr_list[0]));
204:
205: }
206: else {
207:
208: /* this was already a valid IP, so we can just use it as-is. */
209:
210: strcpy (finaddr, addr);
211:
212: }
213:
214: io_sockets[i]->srv.sin_addr.s_addr = inet_addr (finaddr);
215: io_sockets[i]->connected = FALSE;
216:
217: if (io_sockets[i]->sck == -1) {
218: merr_raise (SCKCREAT);
219: merr_set_iochan_err (channel, SCKCREAT, "error creating socket");
220: return 0;
221: }
222:
223: return i;
224:
225: #else
226:
227: merr_raise (SCKCREAT);
228: merr_set_iochan_err (channel, SCKCREAT, "error creating socket");
229: return 0;
230:
231: #endif
232:
233: }
234:
235: short msck_connect (int channel)
236: {
237:
238: #if !defined(MSDOS)
239:
240: short i = channel + FIRSTSCK;
241:
242: if (io_sockets[i]->typ != SOCK_STREAM) {
243: merr_raise (SCKAERR);
244: merr_set_iochan_err (channel, SCKAERR, "cannot CONNECT a UDP socket");
245: return 0;
246: }
247:
248: if (io_sockets[i]->connected == TRUE) {
249: merr_raise (SCKACON);
250: merr_set_iochan_err (channel, SCKACON, "cannot CONNECT previously-connected socket");
251: return 0;
252: }
253:
254: if (connect (io_sockets[i]->sck, (struct sockaddr *) &(io_sockets[i]->srv), sizeof (io_sockets[i]->srv)) < 0) {
255: merr_raise (SCKCERR);
256: merr_set_iochan_err (channel, SCKCERR, "error in CONNECT");
257: return 0;
258: }
259: else {
260: io_sockets[i]->connected = TRUE;
261: }
262:
263: return i;
264:
265: #else
266:
267: return 0;
268:
269: #endif
270:
271: }
272:
273: short msck_write (int channel, char *buf, short length)
274: {
275:
276: #if !defined(MSDOS)
277:
278: ssize_t ct;
279: short i = channel + FIRSTSCK;
280:
281: if (io_sockets[i]->connected == FALSE && io_sockets[i]->typ != SOCK_DGRAM) {
282:
283: /* throw socket not connected error if not doing UDP */
284: merr_raise (SCKNCON);
285: merr_set_iochan_err (channel, SCKNCON, "TCP socket not connected");
286: return 0;
287:
288: }
289:
290: if ((ct = send (io_sockets[i]->sck, buf, length, 0)) < 0) {
291: merr_raise (SCKESND);
292: merr_set_iochan_err (channel, SCKESND, "error in WRITE to socket");
293: return 0;
294: }
295:
296: return ct;
297:
298: #else
299:
300: merr_raise (SCKNCON);
301: merr_set_iochan_err (channel, SCKNCON, "TCP socket not connected");
302: return 0;
303:
304: #endif
305:
306: }
307:
308: short msck_read (int channel, char *buf, long sck_timeout, short sck_timeoutms, short length)
309: {
310:
311: #if !defined(MSDOS)
312: fd_set fds;
313: short i;
314: struct timeval t;
315: char *terminator;
316: char *rdbuf;
317: char ch;
318: short in_term = 0;
319: short termlen = 0;
320: ssize_t rcvct = 0;
321: ssize_t ct = 0;
322:
323: i = channel + FIRSTSCK;
324: terminator = (char *) malloc (255 * sizeof (char));
325: NULLPTRCHK(terminator,"msck_read");
326:
327: rdbuf = (char *) malloc (length * sizeof (char));
328: NULLPTRCHK(rdbuf,"msck_read");
329:
330: if (io_sockets[i]->connected == FALSE && io_sockets[i]->typ != SOCK_DGRAM) {
331: merr_raise (SCKNCON);
332: merr_set_iochan_err (channel, SCKNCON, "TCP socket not connected");
333: return 0;
334: }
335:
336: buf[0] = '\0';
337:
338: termlen = msck_get_terminator (channel, terminator);
339:
340: for (;;) {
341:
342: FD_ZERO (&fds);
343: FD_SET ((unsigned int) io_sockets[i]->sck, &fds);
344:
345: if (sck_timeout == -1) {
346: /* wait forever */
347: select (io_sockets[i]->sck + 1, &fds, NULL, NULL, NULL);
348: }
349: else {
350:
351: /* set the socket timeout */
352: t.tv_sec = sck_timeout;
353: t.tv_usec = sck_timeoutms * 1000;
354:
355: select (io_sockets[i]->sck + 1, &fds, NULL, NULL, &t);
356:
357: }
358:
359: if (ct >= length) goto read_done;
360:
361: if ((rcvct = recv (io_sockets[i]->sck, rdbuf, 1, 0)) < 1) {
362: merr_raise (SCKERCV);
363: merr_set_iochan_err (channel, SCKERCV, "error in READ from socket");
364: return 0;
365: }
366: else {
367:
368: ct++;
369:
370: ch = rdbuf[0];
371: rdbuf[1] = '\0';
372:
373: }
374:
375: strcat (buf, rdbuf);
376:
377: if (ch == terminator[0] && termlen == 1) {
378: /* 1-char terminator reached. populate $KEY. */
379: sprintf (zb, "%c\201", terminator[0]);
380: goto read_done;
381: }
382: else if (ch == terminator[0] && termlen == 2 && !in_term) {
383: /* first char of 2-char terminator found. we're now in a terminator. */
384: in_term = 1;
385: }
386: else if (ch == terminator[0] && termlen == 2 && in_term) {
387: /* we're in a 2-char terminator, but the second char doesn't match. */
388: in_term = 0;
389: }
390: else if (ch == terminator[1] && termlen == 2 && in_term) {
391: /* 2-char terminator reached. populate $KEY */
392: sprintf (zb, "%s\201", terminator);
393: goto read_done;
394: }
395:
396: }
397:
398: read_done:
399:
400: stcnv_c2m (buf);
401:
402: return ct;
403:
404: #else
405:
406: return 0;
407:
408: #endif
409:
410: }
411:
412: short msck_close (int channel)
413: {
414:
415: #if !defined(MSDOS)
416: short i = channel + FIRSTSCK;
417:
418:
419: if (io_sockets[i] != NULL) {
420:
421: shutdown (io_sockets[i]->sck, SHUT_RDWR);
422:
423: free (io_sockets[i]);
424:
425: return 1;
426: }
427: #endif
428:
429: return 0;
430:
431: }
432:
433: short msck_get_terminator (int channel, char *buf)
434: {
435: char wr_io[9];
436: char *wr_key;// = (char *) malloc (STRLEN * sizeof (char));
437: //NULLPTRCHK(wr_key,"msck_get_terminator");
438:
439: freem_ref_t *wrr = (freem_ref_t *) malloc (sizeof (freem_ref_t));
440: NULLPTRCHK(wrr,"msck_get_terminator");
441:
442: snprintf (wr_io, 8, "%d", channel);
443:
444: /* get ^$DEVICE($IO,"TERMINATOR") */
445:
446: mref_init (wrr, MREF_RT_SSV, "^$DEVICE");
447: mref_set_subscript (wrr, 0, wr_io);
448: mref_set_subscript (wrr, 1, "TERMINATOR");
449:
450: wr_key = mref_to_internal (wrr);
451:
452: ssvn (get_sym, wr_key, buf);
453:
454: if (merr () > OK) {
455:
456: /* SSV node was undefined. Default to CRLF terminator. */
457:
458: sprintf (buf, "\r\n\201");
459: merr_raise (OK);
460:
461: }
462:
463: stcnv_m2c (buf);
464:
465: free (wr_key);
466: free (wrr);
467:
468: return strlen (buf);
469: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>