Annotation of freem/src/io_socket.c, revision 1.1

1.1     ! snw         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>