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