File:  [Coherent Logic Development] / freem / src / io_socket.c
Revision 1.6: download - view: text, annotated - select for diffs
Mon Mar 24 02:55:26 2025 UTC (12 months, 1 week ago) by snw
Branches: MAIN
CVS tags: HEAD
Socket I/O compat fixes for OS/2

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>