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