![]() ![]() | ![]() |
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: *
1.2 ! snw 18: * Author: Serena Willis <snw@coherent-logic.com>
1.1 snw 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: }