File:  [Coherent Logic Development] / freem / src / io_socket.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Sun Jan 19 02:04:04 2025 UTC (14 months, 2 weeks ago) by snw
Branches: CoherentLogicDevelopment
CVS tags: start
FreeM

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

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