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