File:  [Coherent Logic Development] / freem / src / io_socket.c
Revision 1.9: download - view: text, annotated - select for diffs
Sun Apr 13 04:22:43 2025 UTC (3 months, 2 weeks ago) by snw
Branches: MAIN
CVS tags: HEAD
Fix snprintf calls

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

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