Annotation of freem/src/io_socket.c, revision 1.1.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>