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