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>