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