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