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>