source: git/MP/MP/MP_TcpTransp.c @ 7adb502

spielwiese
Last change on this file since 7adb502 was 7adb502, checked in by Olaf Bachmann <obachman@…>, 27 years ago
* removed conflicts from MP_1_1_2_0 conflicts git-svn-id: file:///usr/local/Singular/svn/trunk@444 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 52.3 KB
Line 
1/***********************************************************************
2 *
3 *                    MP version 1.1.2:  Multi Protocol
4 *                    Kent State University, Kent, OH
5 *                 Authors:  S. Gray, N. Kajler, P. Wang
6 *         (C) 1993, 1994, 1995, 1996, 1997 All Rights Reserved
7 *
8 *                                 NOTICE
9 *
10 *  Permission to use, copy, modify, and distribute this software and
11 *  its documentation for non-commercial purposes and without fee is
12 *  hereby granted provided that the above copyright notice appear in all
13 *  copies and that both the copyright notice and this permission notice
14 *  appear in supporting documentation.
15 *
16 *  Neither Kent State University nor the Authors make any representations
17 *  about the suitability of this software for any purpose.  The MP Library
18 *  is distributed in the hope that it will be useful, but is provided  "as
19 *  is" and without any warranty of any kind and without even the implied
20 *  warranty of merchantability or fitness for a particular purpose.
21 *
22 *
23 *   IMPLEMENTATION FILE:  MP_TcpTransp.c
24 *
25 *      Public interface routines to the "tcp" transport device.
26 *      These include read, write, open, close, and init routines.
27 *      There are some support routines as well.
28 *
29 *  Change Log:
30 *       08/16/96   hennig     - tcp transport device ported to Windows95/NT
31 *       11/25/95   sgray      - added support to negotiate endian word order.
32 *       11/20/95   sgray      - added tcp_has_data() routine to determine
33 *                               if there is any data on the link to read and
34 *                               put it in the transp_op structure
35 *       11/15/95   obachman   - use vfork instead of fork
36 *                             - major change to open_tcp_launch_mode:
37 *                             1.) -MPapplication takes one string: application
38 *                                 and its arguments
39 *                             2.) MP appends -MPhost -MPport to arguments of
40 *                                 application
41 *                             3.) rsh is started with -n to disable
42 *                                 standard input
43 *                             4.) rsh has fixed number of args, namely:
44 *                                 -n host application
45 *       11/14/95   obachman   - introduced macro for remote shell command
46 *       11/10/95   obachman   - cleaned up things around the select call,
47 *                               so that it runs under Linux, and uses
48 *                               the timestruct to actually come back after
49 *                               an unsuccessful select
50 *                             - fixed nasty bug on line 348:
51 *                      tcp_rec->peerhost = (char *) malloc(sizeof(host) + 1);
52 *                      should have been
53 *                      tcp_rec->peerhost = (char *) malloc(strlen(host) + 1);
54 *                              - similar in tcp_inti_transport
55 *                              - one minor cast to (Char **) of dmy_args
56 *       September 11, 1995 SG - Reorganization of files.  Cleaned up
57 *                               header files and relocated source to
58 *       3/3/96    sgray - moved call to tcp_init_transport() inside
59 *                         tcp_open_transport() and removed the init
60 *                         routine from the tcp_ops struct.
61 *       5/16/96   sgray - Changed the tcp_read() routine to loop until it
62 *                         has read all the data requested.  This was done
63 *                         b/c the higher level routines in the buffering
64 *                         layer return failure if the number of bytes read
65 *                         is not the number of bytes requested.  We ran into
66 *                         the problem that occasionally the application was
67 *                         trying to read the data faster than the TCP layer
68 *                         could provide it, providing a "false failure".
69 *       3/11/97   sgray - Changed all memory management calls that allocate
70 *                         or deallocate strings to use the "Raw" calls.
71 *
72 ***************************************************************************/
73
74
75#ifndef lint
76static char vcid[] = "@(#) $Id: MP_TcpTransp.c,v 1.1.1.1 1997/06/7 14:05:52 sgr
77ay Exp $";
78#endif /* lint */
79
80#include "MP.h"
81#include <string.h>
82#include <stdlib.h>
83
84#ifdef __WIN32__
85
86#     include <process.h>
87
88#else /* not __WIN32__ */
89
90/*
91 * define here which programm to call for starting a remote shell
92 * and the position of the -n parameter (before or after host ?)
93 */
94#  ifdef hpux
95#     define RSH_CMD "/usr/bin/remsh"
96#     define RSH_N    2
97#  else
98#  ifdef linux
99#     define RSH_CMD "/usr/bin/rsh"
100#     define RSH_N    1
101#  else
102#  ifdef  mips
103#     define RSH_CMD "/usr/ucb/rsh"
104#     define RSH_N    1
105#  else
106#     define RSH_CMD "/usr/ucb/rsh"
107#     define RSH_N    1
108#  endif
109#  endif
110#  endif
111
112#  if RSH_N != 1
113#      define RSH_HOST 1
114#  else
115#      define RSH_HOST 2
116#  endif
117
118#include <unistd.h>
119
120#endif /* not __WIN32__ */
121
122
123
124#define log_msg_len 128
125#define MAX_ARGS     24
126
127static char log_msg[log_msg_len];  /* for event logging */
128
129
130static MP_Status_t tcp_negotiate_word_order _ANSI_ARGS_((MP_Link_pt));
131static MP_Status_t tcp_negotiate_fp_format _ANSI_ARGS_((MP_Link_pt));
132static MP_Status_t tcp_negotiate_bigint_format _ANSI_ARGS_((MP_Link_pt));
133static int get_tcp_mode _ANSI_ARGS_((int, char**));
134
135#ifdef __WIN32__
136static int splitargs _ANSI_ARGS_((char*));
137#endif
138
139
140MP_TranspOps_t tcp_ops = {
141    tcp_write,
142    tcp_read,
143    tcp_flush,
144    tcp_get_status,
145    tcp_open_connection,
146    tcp_close_connection
147};
148
149
150/****  Private routines ******/
151
152/***********************************************************************
153 * FUNCTION:  get_tcp_mode
154 * INPUT:     argc - number of arguments in argv
155 *            argv - arguments as strings
156 * OUTPUT:    Success:  one of MP_CONNECT_MODE, MP_LISTEN_MODE, MP_LAUNCH_MODE.
157 *            Failure:  MP_NO_SUCH_TCP_MODE
158 * OPERATION: Get the argument to the "-MPmode" option and see what it is.
159 ***********************************************************************/
160#ifdef __STDC__
161static int get_tcp_mode(int argc, char **argv)
162#else
163static int get_tcp_mode(argc, argv)
164    int    argc;
165    char **argv;
166#endif
167{
168    char *cmode;
169
170#ifdef MP_DEBUG
171    fprintf(stderr, "get_tcp_mode: entering\n");
172    fflush(stderr);
173#endif
174
175    if ((cmode = IMP_GetCmdlineArg(argc, argv, "-MPmode")) != NULL)
176        if (!strcmp(cmode, "connect"))
177            return MP_CONNECT_MODE;
178        else if (!strcmp(cmode, "listen"))
179            return MP_LISTEN_MODE;
180        else if (!strcmp(cmode, "launch"))
181            return MP_LAUNCH_MODE;
182        else if (!strcmp(cmode, "fork"))
183          return MP_FORK_MODE;
184   
185
186#ifdef MP_DEBUG
187    fprintf(stderr, "get_tcp_mode: exiting\n");
188    fflush(stderr);
189#endif
190
191    return MP_NO_SUCH_TCP_MODE;
192}
193
194
195/***********************************************************************
196 * FUNCTION:  open_tcp_launch_mode
197 * INPUT:     link - pointer to link structure for this connection
198 *            argc - number of arguments in argv
199 *            argv - arguments as strings
200 * OUTPUT:    Success:  MP_Success
201 *                      socket connected to the specified host, port
202 *            Failure:  MP_Failure
203 * OPERATION: Launch an application remotely.  rsh is used for this,
204 *            which isn't portable beyond Unix.  The application name
205 *            must be supplied. First we open a port in listening mode,
206 *            then the application is launched with arguments asking it
207 *            to establish a connection to this host at the opened port.
208 *            Immediately after the launch, we do a blocking accept(),
209 *            waiting for the application to make the connection.
210 *            Unfortunately there may be problems, so maybe a non-blocking
211 *            accept would be better.
212 *
213 *            Windows95/NT: Launching applications is only supported on
214 *            the local machine. This is done using the spawnv function.
215 *            If the "-MPhost <host>" option is given on the command line
216 *            <host> is first checked to see if it is identical to the
217 *            local host.
218 ***********************************************************************/
219#ifdef __STDC__
220MP_Status_t open_tcp_launch_mode(MP_Link_pt   link,
221                                 int          argc,
222                                 char       **argv)
223#else
224MP_Status_t open_tcp_launch_mode(link, argc, argv)
225    MP_Link_pt   link;
226    int          argc;
227    char       **argv;
228#endif
229{
230#ifdef __WIN32__
231    char       myhost[64], cport[10];
232    char     **dmy_args, **spawn_argv, *rhost, *p, *mpapp;
233    int        spawn_pid, i, applen, appopts;
234    MP_TCP_t  *tcp_rec;
235    HOSTENT   *myhostent, *rhostent;
236
237#ifdef MP_DEBUG
238    fprintf(stderr, "open_tcp_launch_mode: entering\n");
239    fflush(stderr);
240#endif /* MP_DEBUG */
241
242    TEST_LINK_NULL(link);
243    tcp_rec = (MP_TCP_t *)link->transp.private1;
244    tcp_rec->peerhost = NULL;
245
246    if (gethostname(myhost, 64) == SOCKET_ERROR)
247        return MP_SetError(link, MP_Failure);
248
249    /*
250     * Let's assume that there is no rsh application available in Windows95/NT.
251     * Therefore, all we can do is spawn a local process. So we must check if
252     * the -MPhost argument is either equal to "localhost", myhost or not
253     * specified at all. If not, we must bail out.
254     */
255    rhost = IMP_GetCmdlineArg(argc, argv, "-MPhost");
256    if (rhost != NULL && strcmp(rhost, "localhost")) {
257
258        /*
259         * OK. So the user absolutely had to specify a host name. Then we have to
260         * find out if myhost and the remote host are really the same machine.
261         *
262         * First, get host information for both host names.
263         */
264        myhostent = gethostbyname(myhost);
265        rhostent  = gethostbyname(rhost);
266
267        if (myhostent == NULL) {
268            MP_LogEvent(link, MP_ERROR_EVENT,
269                        "MP_OpenLink: cannot get local host information.");
270            return MP_SetError(link, MP_Failure);
271        }
272
273        if (rhostent == NULL) {
274            MP_LogEvent(link, MP_ERROR_EVENT,
275                        "MP_OpenLink: cannot get remote host information.");
276            return MP_SetError(link, MP_Failure);
277        }
278
279        /* Now compare both official host names and see if they are equal. */
280        if (strcmp(myhostent->h_name, rhostent->h_name)) {
281            MP_LogEvent(link, MP_ERROR_EVENT,
282                        "MP_OpenLink: launching remote processes is not"
283                        "supported (yet).");
284            return MP_SetError(link, MP_Failure);
285        }
286    }
287
288    /* Get the application to be launched. */
289    p = IMP_GetCmdlineArg(argc, argv, "-MPapplication");
290    if (p == NULL) {
291        MP_LogEvent(link, MP_ERROR_EVENT,
292                    "MP_OpenLink: bad or missing -MPapplication argument");
293        return MP_SetError(link, MP_Failure);
294    }
295
296    /*
297     * If we could execute an rsh application we could simply pass it the
298     * -MPapplication argument string and let the rsh do the parsing. However,
299     * as we need to use the spawnv function to launch the child process we must
300     * split the application's argument string into the argv's ourselves:
301     */
302
303    /* Make a copy of the -MPapplication argument string. */
304    applen = strlen(p) + 1;
305    mpapp = (char*)IMP_RawMemAllocFnc(applen);
306    if (mpapp == NULL) {
307        MP_LogEvent(link, MP_ERROR_EVENT,
308                    "MP_OpenLink: memory allocation error");
309        return MP_SetError(link, MP_MemAlloc);
310    }
311    strcpy(mpapp, p);
312
313    /* Split the argument string into several Null-terminated strings. */
314    appopts = splitargs(mpapp);
315
316    /* Allocate memory for the application's argv array. */
317    spawn_argv = (char**)IMP_MemAllocFnc((appopts + 9) * sizeof(char*));
318    if (spawn_argv == NULL) {
319        MP_LogEvent(link, MP_ERROR_EVENT,
320                    "MP_OpenLink: memory allocation error");
321        return MP_SetError(link, MP_MemAlloc);
322    }
323
324    /* Fill the argv array with pointers to the arguments. */
325    p = mpapp;
326    for (i = 0; i < appopts; i++) {
327        /* Take one argument and put it to the argv's. */
328        spawn_argv[i] = p;
329        /* Advance the pointer to the next argument. */
330        while (*p++);
331    }
332
333    /* User shouldn't have specified a -MPport option, but we'll use it if
334       it is there. */
335    if (IMP_GetCmdlineArg(argc, argv, "-MPport") == NULL) {
336        dmy_args = (char**)IMP_MemAllocFnc((argc + 2) * sizeof(char *));
337        if (dmy_args == NULL) {
338            MP_LogEvent(link, MP_ERROR_EVENT,
339                        "MP_OpenLink: memory allocation error");
340            return MP_SetError(link, MP_MemAlloc);
341        }
342
343        for (i = 0; i < argc; i++)
344            dmy_args[i] = argv[i];
345        dmy_args[i++] = "-MPport";
346        dmy_args[i++] = MP_INIT_PORT;
347
348        if (open_tcp_listen_mode(link, i, dmy_args) != MP_Success) {
349            MP_LogEvent(link, MP_ERROR_EVENT,
350                        "MP_OpenLink: can't open a listening socket");
351            return MP_SetError(link, MP_CantConnect);
352        }
353
354        IMP_MemFreeFnc(dmy_args, (argc + 2) * sizeof(char*));
355    }
356
357    else  /* User specified port number, so we use original argc/argv pair. */
358        if (open_tcp_listen_mode(link, argc, argv) != MP_Success) {
359            MP_LogEvent(link, MP_ERROR_EVENT,
360                        "MP_OpenLink: can't open a listening socket");
361            return MP_SetError(link, MP_CantConnect);
362        }
363
364    sprintf(cport, "%hd", tcp_rec->peerport);
365
366    /* Add the remaining MP options to the application's argument list. */
367    spawn_argv[appopts  ] = "-MPtransp";
368    spawn_argv[appopts+1] = "TCP";
369    spawn_argv[appopts+2] = "-MPmode";
370    spawn_argv[appopts+3] = "connect";
371    spawn_argv[appopts+4] = "-MPhost";
372    spawn_argv[appopts+5] = myhost;
373    spawn_argv[appopts+6] = "-MPport";
374    spawn_argv[appopts+7] = cport;
375    spawn_argv[appopts+8] = NULL;
376
377    /* Launch the application. */
378    spawn_pid = spawnv(P_NOWAIT, spawn_argv[0], spawn_argv);
379    if (spawn_pid == -1) {
380        MP_LogEvent(link, MP_ERROR_EVENT,
381                    "MP_OpenLink: can't launch application");
382        return MP_SetError(link, MP_Failure);
383    }
384
385    /* Free the application argument memory. */
386    IMP_MemFreeFnc(spawn_argv, (appopts + 9) * sizeof(char*));
387    IMP_RawMemFreeFnc(mpapp);
388
389    /* Wait for the child application to connect. */
390    socket_accept_blocking(link, &tcp_rec->sock);
391
392#ifdef MP_DEBUG
393    fprintf(stderr, "open_tcp_launch_mode: exiting\n");
394    fflush(stderr);
395#endif
396
397    RETURN_OK(link);
398
399#else /*  not __WIN32__ */
400
401    char     *rsh_argv[5], myhost[64], cport[5], **dmy_args, *appstr;
402    int       rsh_pid = -1, i;
403    MP_TCP_t *tcp_rec;
404
405#ifdef MP_DEBUG
406    fprintf(stderr, "open_tcp_launch_mode: entering\n");
407    fflush(stderr);
408#endif
409
410    TEST_LINK_NULL(link);
411    tcp_rec = (MP_TCP_t *)link->transp.private1;
412    tcp_rec->peerhost = NULL;
413
414    if (gethostname(myhost, 64) == -1)
415        return MP_SetError(link, MP_Failure);
416
417    /* NOTE: Ultrix doesn't like this order. It expects rsh host [-n] command. */
418
419    rsh_argv[0] = RSH_CMD;
420    rsh_argv[RSH_N] = "-n";
421    rsh_argv[RSH_HOST] = IMP_GetCmdlineArg(argc, argv, "-MPhost");
422    if (rsh_argv[RSH_HOST] == NULL) {
423        MP_LogEvent(link, MP_ERROR_EVENT,
424                    "MP_OpenLink: bad or missing -MPhost argument");
425        return MP_SetError(link, MP_Failure);
426    }
427
428    rsh_argv[3] = IMP_GetCmdlineArg(argc, argv, "-MPapplication");
429    if (rsh_argv[3] == NULL) {
430        MP_LogEvent(link, MP_ERROR_EVENT,
431                    "MP_OpenLink: bad or missing -MPapplication argument");
432        return MP_SetError(link, MP_Failure);
433    }
434
435    /*
436     * User shouldn't have specified a -MPport option, but we'll use it if
437     * it is there.
438     */
439
440    if (IMP_GetCmdlineArg(argc, argv, "-MPport") == NULL) {
441        dmy_args = (char**)IMP_MemAllocFnc((argc + 2) * sizeof(char*));
442        if (dmy_args == NULL) {
443            MP_LogEvent(link, MP_ERROR_EVENT,
444                        "MP_OpenLink: memory allocation error");
445            return MP_SetError(link, MP_MemAlloc);
446        }
447
448        for (i = 0; i < argc; i++)
449            dmy_args[i] = argv[i];
450        dmy_args[i++] = "-MPport";
451        dmy_args[i++] = MP_INIT_PORT;
452
453        if (open_tcp_listen_mode(link, i, dmy_args) != MP_Success) {
454            MP_LogEvent(link, MP_ERROR_EVENT,
455                        "MP_OpenLink: can't open a listening socket");
456            return MP_SetError(link, MP_CantConnect);
457        }
458
459        IMP_MemFreeFnc(dmy_args, (argc + 2) * sizeof(char *));
460    }
461
462    else
463        /* User specified port number, so we use original argc/argv pair. */
464        if (open_tcp_listen_mode(link, argc, argv) != MP_Success) {
465            MP_LogEvent(link, MP_ERROR_EVENT,
466                        "MP_OpenLink: can't open a listening socket");
467            return MP_SetError(link, MP_CantConnect);
468        }
469
470    sprintf(cport, "%hd", tcp_rec->peerport);
471
472    appstr = (char*)IMP_RawMemAllocFnc(strlen(rsh_argv[3]) + 105);
473    strcpy(appstr, rsh_argv[3]);
474    strcat(appstr, " -MPtransp TCP -MPmode connect -MPhost ");
475    strcat(appstr, myhost);
476    strcat(appstr, " -MPport ");
477    strcat(appstr, cport);
478
479    rsh_argv[3] = appstr;
480    rsh_argv[4] = NULL;
481
482    if ((rsh_pid = vfork()) == -1) {
483        MP_LogEvent(link, MP_ERROR_EVENT,
484                    "MP_OpenLink: can't fork to launch application");
485        return MP_SetError(link, MP_Failure);
486    }
487
488    if (!rsh_pid) {
489        execvp(rsh_argv[0], rsh_argv);
490        fputs("open_tcp_launch_mode: execvp failed - bailing out\n", stderr);
491        fflush(stderr);
492        _exit(1);
493    }
494
495    socket_accept_blocking(link, &tcp_rec->sock);
496
497    IMP_RawMemFreeFnc(appstr);
498
499#ifdef MP_DEBUG
500    fprintf(stderr, "open_tcp_launch_mode: exiting\n");
501    fflush(stderr);
502#endif
503
504    RETURN_OK(link);
505#endif /* not __WIN32__ */
506}
507
508
509
510/***********************************************************************
511 * FUNCTION:  open_tcp_listen_mode
512 * INPUT:     link - pointer to link structure for this connection
513 *            argc - number of arguments in argv
514 *            argv - arguments as strings
515 * OUTPUT:    Success:  MP_Success
516 *                      socket connected to the specified host, port
517 *            Failure:  MP_Failure
518 * OPERATION: Open a socket in the AF_INET domain, verify that the port
519 *            is in the legal range (don't annoy anyone), bind the socket,
520 *            finally, do a listen.  We update the link structure with
521 *            the socket and port information.
522 ***********************************************************************/
523#ifdef __STDC__
524MP_Status_t open_tcp_listen_mode(MP_Link_pt   link,
525                                 int          argc,
526                                 char       **argv)
527#else
528MP_Status_t open_tcp_listen_mode(link, argc, argv)
529    MP_Link_pt   link;
530    int          argc;
531    char       **argv;
532#endif
533{
534    SOCKADDR_IN  isin;
535    SOCKET       sock;
536    int          attempts = 0;
537    char        *cport;
538    short        port;
539    MP_TCP_t    *tcp_rec = (MP_TCP_t *)link->transp.private1;
540
541#ifdef MP_DEBUG
542    fprintf(stderr, "open_tcp_listen_mode: entering\n");
543    fflush(stderr);
544#endif /* MP_DEBUG */
545
546    tcp_rec->peerhost = NULL;
547
548    if ((cport = IMP_GetCmdlineArg(argc, argv, "-MPport")) == NULL)
549        return MP_SetError(link, MP_Failure);
550
551    port = atoi(cport);
552    if (port < IPPORT_RESERVED)
553        port = IPPORT_RESERVED;
554
555    sock = socket(AF_INET, SOCK_STREAM, 0);
556    if (sock == INVALID_SOCKET) {
557        sprintf(log_msg,
558                "open_tcp_listen_mode: "
559                "Cannot open stream socket (socket error %d)",
560                LASTERROR);
561        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
562        return MP_SetError(link, MP_Failure);
563    }
564
565    /* Initialize socket's address structure */
566    isin.sin_family      = AF_INET;
567    isin.sin_addr.s_addr = htonl(INADDR_ANY);
568    isin.sin_port        = htons(port);
569
570    while (bind(sock, (SOCKADDR*)&isin, sizeof(isin)) == SOCKET_ERROR) {
571        if (LASTERROR == ERRORCODE(EADDRINUSE)) {
572            port++;
573
574            /* since port is an unsigned short, it may wrap around */
575            if (port < IPPORT_RESERVED)
576                port = IPPORT_RESERVED;
577
578            isin.sin_port = htons(port);
579            attempts = 0;
580        }
581
582        if (attempts++ > MP_MAX_BIND_ATTEMPTS) {
583            MP_LogEvent(link, MP_ERROR_EVENT,
584                        "open_tcp_listen_mode: Cannot bind");
585            return MP_SetError(link, MP_Failure);
586        }
587    }
588
589    /* Now listen for the anticipated connect request */
590    listen(sock, 1);
591    tcp_rec->bindsock = sock;
592    tcp_rec->sock     = sock;
593    tcp_rec->peerport = port;
594
595#ifndef NO_LOGGING
596    if (link->logmask & MP_LOG_INIT_EVENTS) {
597        sprintf(log_msg,
598                "open_tcp_listen_mode: socket -> %d, port -> %d",
599                sock, port);
600        MP_LogEvent(link, MP_INIT_EVENT, log_msg);
601    }
602#endif /* NO_LOGGING */
603
604#ifdef MP_DEBUG
605    fprintf(stderr, "\t  tcp_rec->sock = %d\n", tcp_rec->sock);
606    fprintf(stderr, "\t  tcp_rec->port = %d\n", tcp_rec->peerport);
607    fprintf(stderr,
608            "open_tcp_listen_mode: exiting - socket = %d, port = %d\n",
609            sock, port);
610    fflush(stderr);
611#endif
612
613    RETURN_OK(link);
614}
615
616
617
618/***********************************************************************
619 * FUNCTION:  open_tcp_fork_mode
620 * INPUT:     link - pointer to link structure for this connection
621 * OUTPUT:    Success:  MP_Success
622 *                      socket connected to the a forked child
623 *            Failure:  MP_Failure - couldn't make the connection.
624 * OPERATION: forks a child, establishes a connection to it and uses
625 *            the return of the fork to set parent/child attribute
626 ***********************************************************************/
627#ifdef __STDC__
628MP_Status_t open_tcp_fork_mode(MP_Link_pt link, int argc, char** argv)
629#else
630MP_Status_t open_tcp_fork_mode(link, argc, argv)
631    MP_Link_pt   link;
632    int          argc;
633    char**       argv;
634#endif
635{
636  int pid;
637  MP_TCP_t *tcp_rec = (MP_TCP_t *) link->transp.private1;
638#ifndef NO_LOGGING
639  char log_msg[200];
640  char *parent_logfilename = link->env->logfilename;
641  FILE *parent_logfd = link->env->logfd;
642#endif
643 
644  /* prepare the socket */
645  if (open_tcp_listen_mode(link, argc, argv) != MP_Success)
646  {
647    MP_LogEvent(link, MP_ERROR_EVENT,
648                "MP_OpenLink: can't open a listening socket");
649    return MP_SetError(link, MP_CantConnect);
650  }
651
652#ifndef NO_LOGGING
653  /* Open the log file before the fork, so that the processes do not
654     get out of sync */
655  sprintf(log_msg,
656          "open_tcp_fork: Preparing to create fork link, parent id: %d",
657          getpid());
658  if (open_logfile(link->env) != MP_Success) return MP_Failure;
659  MP_LogEvent(link, MP_INIT_EVENT,
660              "open_tcp_fork: MP Log file for a forked (child) link");
661  sprintf(log_msg, "open_tcp_fork: Parent link: L%d, logfile: %s, pid: %d",
662          link->link_id, parent_logfilename, getpid());
663  MP_LogEvent(link, MP_INIT_EVENT, log_msg);
664#endif
665 
666  /* Do the fork */
667  if ((pid = fork()) == -1)
668  {
669#ifndef NO_LOGGING
670    link->env->logfilename = parent_logfilename;
671    link->env->logfd = parent_logfd;
672#endif   
673    MP_LogEvent(link, MP_ERROR_EVENT, "MP_OpenLink: can't fork");
674    return MP_SetError(link, MP_Failure);
675  }
676
677  if (!pid)
678  {
679    /* Child's business */
680    MP_Status_t stat;
681    char cport[12];
682    char *argv[] = {"MPtransp","TCP","-MPmode","connect","-MPhost",
683                    "localhost", "-MPport", ""};
684    sprintf(cport,"%d",tcp_rec->peerport);
685    argv[7] = cport;
686
687    /* establish connection */
688    stat = open_tcp_connect_mode(link, 8, argv);
689
690#ifndef NO_LOGGING
691    IMP_RawMemFreeFnc(parent_logfilename);
692    if (stat == MP_Success)
693      sprintf(log_msg, "open_tcp_fork: opened forked link, child id: %d",
694              getpid());
695    else
696      sprintf(log_msg,
697              "open_tcp_fork: open of forked link failed, child is exited");
698    MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
699#endif
700    if (stat != MP_Success)
701      _exit(1);
702    else
703    {
704      tcp_rec->status = MP_LinkIsChild;
705      return MP_Success;
706    }
707  }
708  else
709  {
710    /* parent's business */
711#ifndef NO_LOGGING
712    /* reset logging stuff */
713    link->env->logfilename = parent_logfilename;
714    link->env->logfd = parent_logfd;
715#endif
716
717    /* establish connection */
718    socket_accept_blocking(link, &tcp_rec->sock);
719    tcp_rec->status = MP_LinkIsParent;
720
721#ifndef NO_LOGGING
722    sprintf(log_msg,"open_tcp_fork: opened fork link, parent id: %d",
723            getpid()); 
724    MP_LogEvent(link, MP_INIT_EVENT, log_msg);
725#endif
726
727    RETURN_OK(link);
728  }
729}
730 
731/***********************************************************************
732 * FUNCTION:  open_tcp_connect_mode
733 * INPUT:     link - pointer to link structure for this connection
734 *            argc - number of arguments in argv
735 *            argv - arguments as strings
736 * OUTPUT:    Success:  MP_Success
737 *                      socket connected to the specified host, port
738 *            Failure:  MP_Failure - couldn't make the connection.
739 *                      Maybe the peer isn't listening.
740 * OPERATION: Establish a TCP connection to the peer's port. Update the
741 *            private tcp structure accordingly.  We hope that there
742 *            is a peer actually listening on the given port.  We make
743 *            1000 attempts before giving up.
744 ***********************************************************************/
745#ifdef __STDC__
746MP_Status_t open_tcp_connect_mode(MP_Link_pt   link,
747                                  int          argc,
748                                  char       **argv)
749#else
750MP_Status_t open_tcp_connect_mode(link, argc, argv)
751    MP_Link_pt   link;
752    int          argc;
753    char       **argv;
754#endif
755{
756    SOCKET       sock;
757    int          attempts = 0;
758    HOSTENT     *hp;
759    SOCKADDR_IN  isin;
760    char        *host, *cport, *ptr;
761    short        port;
762    MP_TCP_t    *tcp_rec = (MP_TCP_t *)link->transp.private1;
763
764#ifdef MP_DEBUG
765    fprintf(stderr, "open_tcp_connect_mode: entering\n");
766    fflush(stderr);
767#endif /* MP_DEBUG */
768
769    tcp_rec->peerhost = NULL;
770
771    /* get the host and port number */
772    if ((host = IMP_GetCmdlineArg(argc, argv, "-MPhost")) == NULL
773        || (cport = IMP_GetCmdlineArg(argc, argv, "-MPport")) == NULL)
774        return MP_SetError(link, MP_Failure);
775
776    port = (short) strtol(cport, &ptr, 10);
777    if (ptr == cport || errno == ERANGE)
778      return MP_SetError(link, MP_Failure);
779
780    if ((hp = gethostbyname(host)) == NULL) {
781        MP_LogEvent(link, MP_ERROR_EVENT,
782                    "open_tcp_connect_mode - cannot get hostname");
783        return MP_SetError(link, MP_Failure);
784    }
785
786    sock = socket(AF_INET, SOCK_STREAM, 0);
787    if (sock == INVALID_SOCKET) {
788        sprintf(log_msg,
789                "open_tcp_connect_mode - Cannot open socket (socket error %d)",
790                LASTERROR);
791        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
792        return MP_SetError(link, MP_Failure);
793    }
794
795    /* Initialize the socket address to the server's address. */
796    memset((char *)&isin, 0, sizeof(isin));
797    isin.sin_family = AF_INET;
798    memcpy(&isin.sin_addr.s_addr, hp->h_addr, hp->h_length);
799    isin.sin_port = htons(port);
800
801    /* Connect to the server. */
802    while (connect(sock, (SOCKADDR*)&isin, sizeof(isin)) == SOCKET_ERROR) {
803        CLOSE_SOCKET(sock);
804        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
805            sprintf(log_msg,
806                    "open_tcp_connect_mode "
807                    "- Cannot open socket (socket error %d)", LASTERROR);
808            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
809            return MP_SetError(link, MP_Failure);
810        }
811
812        if (attempts > MP_MAX_BIND_ATTEMPTS) {
813            MP_LogEvent(link, MP_ERROR_EVENT,
814                        "open_tcp_connect_mode - Cannot connect");
815            return MP_SetError(link, MP_Failure);
816        }
817
818        attempts++;
819    }
820
821    if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&sock, sizeof(sock))
822        == SOCKET_ERROR) {
823        sprintf(log_msg,
824                "open_tcp_connect_mode "
825                "- cannot set socket option (socket error %d)", LASTERROR);
826        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
827        return MP_SetError(link, MP_Failure);
828    }
829
830    tcp_rec->bindsock = INVALID_SOCKET;
831    tcp_rec->sock     = sock;
832    tcp_rec->peerport = port;
833
834    tcp_rec->peerhost = (char*)IMP_RawMemAllocFnc(strlen(host) +1);
835    TEST_MEM_ALLOC_ERROR(link, tcp_rec->peerhost);
836    strcpy(tcp_rec->peerhost, host);
837
838    tcp_rec->myhost = (char*)IMP_RawMemAllocFnc(MP_HOST_NAME_LEN);
839    TEST_MEM_ALLOC_ERROR(link, tcp_rec->myhost);
840    if (gethostname(tcp_rec->myhost, MP_HOST_NAME_LEN) == SOCKET_ERROR) {
841        IMP_RawMemFreeFnc(tcp_rec->peerhost);
842        IMP_RawMemFreeFnc(tcp_rec->myhost);
843        tcp_rec->myhost = NULL;
844    }
845
846#ifdef MP_DEBUG
847    fprintf(stderr, "open_tcp_connect_mode: exiting - socket is %d\n", sock);
848    fflush(stderr);
849#endif
850
851    RETURN_OK(link);
852}
853
854
855
856/***********************************************************************
857 * FUNCTION:  socket_accept_non_blocking
858 * INPUT:     sock   - the socket to do the accept on
859 * OUTPUT:    Success: the socket to be used for io
860 *                     Failure: MP_Failure
861 *                     sock contains the socket number of the accepted
862 *                     socket.
863 * PURPOSE:   Perform a non-blocking accept.  If we timeout before getting
864 *            a connection request, return MP_Failure.
865 ***********************************************************************/
866#ifdef __STDC__
867MP_Status_t socket_accept_non_blocking(MP_Link_pt link, SOCKET *sock)
868#else
869MP_Status_t socket_accept_non_blocking(link, sock)
870    MP_Link_pt  link;
871    SOCKET     *sock;
872#endif
873{
874    SOCKADDR addr;
875    SOCKET   nsock = 0;
876    int      attempts, addrlen = sizeof(addr);
877    fd_set   read_template;
878    TIMEVAL  timeout;
879
880#ifdef MP_DEBUG
881    fprintf(stderr,
882            "socket_accept_non_blocking: entering - socket = %d\n", *sock);
883    fflush(stderr);
884#endif
885
886    timeout.tv_sec  = MP_ACCEPT_WAIT_SEC;
887    timeout.tv_usec = MP_ACCEPT_WAIT_USEC;
888
889    for (attempts = 0; attempts < 2; attempts++) {
890        FD_ZERO(&read_template);
891        FD_SET(*sock, &read_template);
892
893        if (SELECT(FD_SETSIZE, &read_template, NULL, NULL, &timeout)
894            != SOCKET_ERROR) {
895
896            if ((nsock = accept(*sock, &addr, &addrlen)) != INVALID_SOCKET)
897                break;
898
899            sprintf(log_msg,
900                    "socket_accept_non_blocking: failed (socket error %d)",
901                    LASTERROR);
902            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
903            return MP_Failure;
904        }
905    }
906
907    *sock = nsock;
908
909#ifdef MP_DEBUG
910    fprintf(stderr,
911            "socket_accept_non_blocking: returning - new socket =  %d\n",
912            *sock);
913    fflush(stderr);
914#endif
915
916    return MP_Success;
917}
918
919
920
921/***********************************************************************
922 * FUNCTION:  socket_accept_blocking
923 * INPUT:     link - link with which sock will be associated, need this
924 *                   really only for the error handling.
925 *            sock - the socket to do the accept on
926 * OUTPUT:    int - Success: the socket to be used for io
927 *                  Failure: MP_Failure
928 * PURPOSE:   Perform a blocking accept.  We won't return until we get an
929 *            connect request or are interrupted.
930 ***********************************************************************/
931#ifdef __STDC__
932MP_Status_t socket_accept_blocking(MP_Link_pt link, SOCKET *sock)
933#else
934MP_Status_t socket_accept_blocking (link, sock)
935    MP_Link_pt  link;
936    SOCKET     *sock;
937#endif
938{
939    SOCKADDR addr;
940    SOCKET   nsock = 0;
941    int      attempts, addrlen = sizeof(addr);
942    fd_set   read_template;
943
944#ifdef MP_DEBUG
945    fprintf(stderr, "socket_accept_blocking: entering - sock = %d\n", *sock);
946    fflush(stderr);
947#endif
948
949    for (attempts = 0; attempts < 2; attempts++) {
950        FD_ZERO(&read_template);
951        FD_SET(*sock, &read_template);
952
953        if (SELECT(FD_SETSIZE, &read_template, NULL, NULL, NULL)
954            != SOCKET_ERROR) {
955            if ((nsock = accept(*sock, &addr, &addrlen)) != INVALID_SOCKET)
956                break;
957
958            sprintf(log_msg,
959                    "socket_accept_blocking: failed (socket error %d)",
960                    LASTERROR);
961            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
962            return MP_Failure;
963        }
964    }
965
966    *sock = nsock;
967
968#ifdef MP_DEBUG
969    fprintf(stderr, "socket_accept_blocking: returning - new socket =  %d\n", *sock);
970    fflush(stderr);
971#endif
972
973    return MP_Success;
974}
975
976
977
978
979
980/****  TCP "public" routines ******/
981
982#ifdef __STDC__
983MP_Boolean_t tcp_get_status(MP_Link_pt link, MP_LinkStatus_t status_to_check)
984#else
985MP_Boolean_t tcp_get_status(link, status_to_check)
986    MP_Link_pt       link;
987    MP_LinkStatus_t  status_to_check;
988#endif
989{
990    SOCKET  sock = ((MP_TCP_t *)link->transp.private1)->sock;
991    fd_set  mask, fdmask;
992    TIMEVAL wt;
993
994#ifdef MP_DEBUG
995    printf("tcp_get_status: entering for link %d\n", link->link_id);
996#endif
997  if (status_to_check == MP_LinkIsParent)
998  {
999    if (((MP_TCP_t *)link->transp.private1)->status == MP_LinkIsParent)
1000      return MP_TRUE;
1001    else
1002      return MP_FALSE;
1003  }
1004  else if (status_to_check == MP_LinkIsChild)
1005  {
1006    if (((MP_TCP_t *)link->transp.private1)->status == MP_LinkIsChild)
1007      return MP_TRUE;
1008    else
1009      return MP_FALSE;
1010  }
1011 
1012
1013    FD_ZERO(&mask);
1014    FD_SET(sock, &mask);
1015
1016    fdmask = mask;
1017
1018    /* Don't block. Return socket status immediately. */
1019    wt.tv_sec  = 0;
1020    wt.tv_usec = 0;
1021
1022    switch (status_to_check) {
1023    case MP_LinkReadyReading:
1024        switch (SELECT(FD_SETSIZE, &fdmask, NULL, NULL, &wt)) {
1025        case 0:
1026            return MP_FALSE;
1027        case SOCKET_ERROR:
1028            return MP_FALSE;
1029        }
1030        if (FD_ISSET(sock, &fdmask))
1031            return MP_TRUE;
1032
1033        return MP_FALSE;
1034
1035    case MP_LinkReadyWriting:
1036        switch (SELECT(FD_SETSIZE, NULL, &fdmask, NULL, &wt)) {
1037        case 0:
1038            return MP_FALSE;
1039        case SOCKET_ERROR:
1040            return MP_FALSE;
1041        }
1042        if (FD_ISSET(sock, &fdmask))
1043            return MP_TRUE;
1044
1045        return MP_FALSE;
1046
1047    default:
1048        sprintf(log_msg,
1049                "tcp_get_status: illegal option %d", status_to_check);
1050        MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1051    }
1052
1053#ifdef MP_DEBUG
1054    printf("tcp_get_status: exiting\n");
1055#endif
1056
1057    return MP_FALSE;
1058}
1059
1060
1061
1062#ifdef __STDC__
1063long tcp_read(MP_Link_pt link, char * buf, long len)
1064#else
1065long tcp_read(link, buf, len)
1066    MP_Link_pt   link;
1067    char *      buf;
1068    long         len;
1069#endif
1070{
1071    SOCKET   sock = ((MP_TCP_t *)link->transp.private1)->sock;
1072    fd_set   mask, readfs;
1073    long     total = 0, n;
1074
1075#ifdef MP_DEBUG
1076    fprintf(stderr, "tcp_read: entering - len = %ld\n", len);
1077    fflush(stderr);
1078#endif
1079
1080    while (len > 0) {
1081        FD_ZERO(&mask);
1082        FD_SET(sock, &mask);
1083
1084        while (MP_TRUE) {
1085            readfs = mask;
1086
1087            /* Do blocking read. */
1088            switch (SELECT(FD_SETSIZE, &readfs, NULL, NULL, NULL)) {
1089            case 0:
1090                MP_LogEvent(link, MP_ERROR_EVENT, "tcp_read: timed out");
1091                return -1;
1092
1093            case SOCKET_ERROR:
1094                if (LASTERROR == ERRORCODE(EINTR))
1095                    continue;
1096                return -1;
1097            }
1098
1099            if (FD_ISSET(sock, &readfs))
1100                break;
1101        }
1102
1103        if ((n = READ_SOCKET(sock, buf, len)) == SOCKET_ERROR) {
1104            sprintf(log_msg, "tcp_read: can't do read (socket error %d)",
1105                    LASTERROR);
1106            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1107            return MP_SetError(link, MP_CantReadLink);
1108        }
1109
1110        total += n;
1111        len   -= n;
1112        buf   += n;
1113    }
1114
1115#ifndef NO_LOGGING
1116    if (link->logmask & MP_LOG_READ_EVENTS) {
1117        sprintf(log_msg, "tcp_read: read %ld bytes", total);
1118        MP_LogEvent(link, MP_READ_EVENT, log_msg);
1119    }
1120#endif /* NO_LOGGING */
1121
1122#ifdef MP_DEBUG
1123    fprintf(stderr, "tcp_read: exiting - read %ld bytes \n", total);
1124    fflush(stderr);
1125#endif /* MP_DEBUG */
1126
1127    return total;
1128}
1129
1130
1131MP_Status_t
1132#ifdef __STDC__
1133tcp_flush(MP_Link_pt link)
1134#else
1135tcp_flush(link)
1136    MP_Link_pt  link;
1137#endif
1138{
1139
1140#ifdef MP_DEBUG
1141    fprintf(stderr, "tcp_flush: entering\n");
1142    fflush(stderr);
1143#endif
1144#ifdef MP_DEBUG
1145    fprintf(stderr, "tcp_flush: exiting  \n");
1146    fflush(stderr);
1147#endif
1148  return MP_ClearError(link);
1149}
1150
1151
1152#ifdef __STDC__
1153long tcp_write(MP_Link_pt link,
1154               char *    buf,
1155               long       len)
1156#else
1157long tcp_write(link, buf, len)
1158    MP_Link_pt  link;
1159    char *     buf;
1160    long        len;
1161#endif
1162{
1163    long   i, cnt;
1164    SOCKET sock = ((MP_TCP_t *)link->transp.private1)->sock;
1165    fd_set mask, writefs;
1166
1167#ifdef MP_DEBUG
1168    fprintf(stderr, "tcp_write: entering - len = %ld\n", len);
1169    fflush(stderr);
1170#endif
1171
1172    FD_ZERO(&mask);
1173    FD_SET(sock, &mask);
1174
1175    while (MP_TRUE) {
1176        writefs = mask;
1177        switch (SELECT(FD_SETSIZE, NULL, &writefs, NULL, NULL)) {
1178        case 0:
1179            MP_LogEvent(link, MP_ERROR_EVENT, "tcp_write: timed out");
1180            return -1;
1181
1182        case SOCKET_ERROR:
1183            if (LASTERROR == ERRORCODE(EINTR))
1184                continue;
1185            MP_LogEvent(link, MP_ERROR_EVENT,
1186                        "tcp_write: errno isn't EINTR - this is bad");
1187            return -1;
1188        }
1189
1190        if (FD_ISSET(sock, &writefs))
1191            break;
1192    }
1193
1194
1195    for (cnt = len; cnt > 0; cnt -= i, buf += i) {
1196        if ((i = WRITE_SOCKET(sock, buf, cnt)) == SOCKET_ERROR) {
1197            sprintf(log_msg, "tcp_write: can't do write (socket error %d)",
1198                    LASTERROR);
1199            MP_LogEvent(link, MP_ERROR_EVENT, log_msg);
1200            return MP_SetError(link, MP_CantWriteLink);
1201        }
1202
1203#ifndef NO_LOGGING
1204        if (link->logmask & MP_LOG_WRITE_EVENTS) {
1205            sprintf(log_msg, "tcp_write: wrote %ld bytes", i);
1206            MP_LogEvent(link, MP_WRITE_EVENT, log_msg);
1207        }
1208#endif /* NO_LOGGING */
1209    }
1210
1211#ifdef MP_DEBUG
1212    fprintf(stderr, "tcp_write: exiting - wrote %ld bytes \n", i);
1213    fflush(stderr);
1214#endif
1215
1216    return len;
1217}
1218
1219
1220
1221/***********************************************************************
1222 * FUNCTION:  tcp_init_transport
1223 * INPUT:     link - pointer to the link structure for this connection
1224 * OUTPUT:    Success:  MP_Success and link structure initialized for the
1225 *                      tcp connection.
1226 *            Failure:  MP_Failure
1227 * OPERATION: If there are no problems, allocate a tcp structure and
1228 *            attach it to the private pointer inside link.
1229 ***********************************************************************/
1230#ifdef __STDC__
1231MP_Status_t tcp_init_transport(MP_Link_pt link)
1232#else
1233MP_Status_t tcp_init_transport(link)
1234    MP_Link_pt link;
1235#endif
1236{
1237    MP_TCP_t *tcp_rec;
1238
1239#ifdef MP_DEBUG
1240    fprintf(stderr, "tcp_init_transport: entering\n"); fflush(stderr);
1241#endif
1242
1243    TEST_LINK_NULL(link);
1244    link->transp.transp_dev =  MP_TcpTransportDev;
1245    link->transp.transp_ops = &tcp_ops;
1246
1247    tcp_rec = (MP_TCP_t*)IMP_MemAllocFnc(sizeof(MP_TCP_t));
1248    TEST_MEM_ALLOC_ERROR(link, tcp_rec);
1249
1250    /* Initialize TCP record. */
1251    tcp_rec->bindsock = INVALID_SOCKET;
1252    tcp_rec->sock     = INVALID_SOCKET;
1253    tcp_rec->peerport = 0;
1254    tcp_rec->myhost   = NULL;
1255    tcp_rec->peerhost = NULL;
1256    tcp_rec->status   = MP_UnknownStatus;
1257
1258    link->transp.private1 = (char *)tcp_rec;
1259
1260#ifdef MP_DEBUG
1261    fprintf(stderr, "tcp_init_transport: exiting\n");
1262    fflush(stderr);
1263#endif
1264
1265    RETURN_OK(link);
1266}
1267
1268/***********************************************************************
1269 * FUNCTION:  tcp_close_connection
1270 * INPUT:     link  - pointer to the link structure for this connection
1271 * OUTPUT:    Success:  MP_Success
1272 *                      Release the tcp structure pointed to by private1.
1273 *            Failure:  MP_Failure
1274 * OPERATION:
1275 ***********************************************************************/
1276#ifdef __STDC__
1277MP_Status_t tcp_close_connection(MP_Link_pt link)
1278#else
1279MP_Status_t tcp_close_connection(link)
1280    MP_Link_pt link;
1281#endif
1282{
1283    MP_TCP_t *tcp_rec;
1284
1285#ifdef MP_DEBUG
1286    fprintf(stderr, "tcp_close_connection: entering\n");
1287    fflush(stderr);
1288#endif /* MP_DEBUG */
1289
1290    TEST_LINK_NULL(link);
1291    if (link->transp.private1 == NULL)
1292        return MP_SetError(link, MP_NullTransport);
1293    tcp_rec = (MP_TCP_t*)link->transp.private1;
1294
1295    CLOSE_SOCKET(tcp_rec->sock);
1296    if (tcp_rec->bindsock != INVALID_SOCKET)
1297        CLOSE_SOCKET(tcp_rec->bindsock);
1298
1299    if (tcp_rec->myhost != NULL)
1300        IMP_RawMemFreeFnc(tcp_rec->myhost);
1301    if (tcp_rec->peerhost != NULL)
1302        IMP_RawMemFreeFnc(tcp_rec->peerhost);
1303    IMP_MemFreeFnc(tcp_rec, sizeof(MP_TCP_t));
1304    link->transp.private1 = NULL;
1305
1306#ifdef MP_DEBUG
1307    fprintf(stderr, "tcp_close_connection: exiting\n");
1308    fflush(stderr);
1309#endif /* MP_DEBUG */
1310
1311    RETURN_OK(link);
1312}
1313
1314
1315
1316
1317/***********************************************************************
1318 * FUNCTION:  tcp_open_connection
1319 * INPUT:     link - pointer to link structure for this connection
1320 *            argc - number of arguments in argv
1321 *            argv - arguments as strings
1322 * OUTPUT:    Success:  MP_Success
1323 *                      link established as a tcp connection
1324 *            Failure:  MP_Failure
1325 * OPERATION: Determine the tcp mode we are using and attempt to
1326 *            establish a connection accordingly.  We could fail
1327 *            for any of a number of reasons.  Bad hostname, bad
1328 *            port number, missing arguments....
1329 ***********************************************************************/
1330#ifdef __STDC__
1331MP_Status_t tcp_open_connection(MP_Link_pt   link,
1332                                int          argc,
1333                                char       **argv)
1334#else
1335MP_Status_t tcp_open_connection(link, argc, argv)
1336    MP_Link_pt   link;
1337    int          argc;
1338    char       **argv;
1339#endif
1340{
1341    MP_Status_t status;
1342    struct protoent *protostruct = NULL;
1343    int sockopt; 
1344
1345#ifdef MP_DEBUG
1346    fprintf(stderr, "tcp_open_connection: entering\n");
1347    fflush(stderr);
1348#endif
1349
1350    TEST_LINK_NULL(link);
1351    if (tcp_init_transport(link) != MP_Success)
1352        return MP_Failure;
1353
1354    switch (get_tcp_mode(argc, argv)) {
1355    case MP_LISTEN_MODE:
1356        status = open_tcp_listen_mode(link, argc, argv);
1357        if (status != MP_Success)
1358            break;
1359        status = socket_accept_blocking(link,
1360                        &((MP_TCP_t *)link->transp.private1)->sock);
1361        break;
1362
1363    case MP_CONNECT_MODE:
1364        status = open_tcp_connect_mode(link, argc, argv);
1365        break;
1366
1367    case MP_LAUNCH_MODE:
1368        status = open_tcp_launch_mode(link, argc, argv);
1369        break;
1370
1371        case MP_FORK_MODE:
1372          status = open_tcp_fork_mode(link, argc, argv);
1373          break;
1374         
1375    default:
1376        MP_LogEvent(link, MP_ERROR_EVENT,
1377                    "Can't open connection, unknown -MPmode argument");
1378        return MP_SetError(link, MP_Failure);
1379    }
1380
1381    if (status != MP_Success)
1382        return MP_Failure;
1383
1384    /* get the protocol number for TCP from /etc/protocols */
1385    if ((protostruct = getprotobyname("tcp")) == NULL) {
1386      MP_LogEvent(link, MP_ERROR_EVENT,
1387                    "Can't open connection: can't get TCP protocol info!!");
1388      return MP_SetError(link, MP_Failure);
1389      }
1390
1391    /* set socket to not coalesce small amounts of data in a single packet */
1392    if (setsockopt(((MP_TCP_t *)link->transp.private1)->sock, 
1393                   protostruct->p_proto, TCP_NODELAY, &sockopt, sizeof(int)) == -1) {
1394      MP_LogEvent(link, MP_ERROR_EVENT,
1395                 "Can't open connection: can't set NODELAY option on socket"); 
1396      return MP_SetError(link, MP_Failure);
1397      }
1398
1399    ERR_CHK(tcp_negotiate_word_order(link));
1400    ERR_CHK(tcp_negotiate_fp_format(link));
1401    ERR_CHK(tcp_negotiate_bigint_format(link));
1402
1403#ifdef MP_DEBUG
1404    fprintf(stderr, "tcp_open_connection: exiting\n");
1405    fflush(stderr);
1406#endif
1407
1408    RETURN_OK(link);
1409}
1410
1411
1412
1413
1414/*
1415 * Currently this is a canned routine.  There is little parsing.  It
1416 * assumes that the first thing the two partners will do is to negotiate
1417 * the word order.  If that is a correct assumption, this should work
1418 * like a champ, otherwise we are in trouble.
1419 */
1420#ifdef __STDC__
1421static MP_Status_t tcp_negotiate_word_order(MP_Link_pt link)
1422#else
1423static MP_Status_t tcp_negotiate_word_order(link)
1424    MP_Link_pt link;
1425#endif
1426{
1427   MP_DictTag_t   dtag;
1428   MP_NumAnnot_t  na;
1429   MP_NumChild_t  nc;
1430   unsigned long  req_word_order;
1431   MP_Common_t    mp_op;
1432
1433   /*
1434    * Step 1:  We send our word order preference to our partner
1435    */
1436   ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1437              MP_CopMpByteOrderRequest, 0, 1)); 
1438   ERR_CHK(IMP_PutUint32(link, (unsigned long)link->env->native_word_order));
1439   MP_EndMsgReset(link);
1440
1441   /*
1442    * Step 2: Get a reply.  It should be MP_CopMpByteOrderRequest.
1443    * Probably should peek at the data stream here to be sure we are
1444    * getting the right thing.
1445    */
1446   ERR_CHK(MP_SkipMsg(link));
1447   ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1448   ERR_CHK(IMP_GetUint32(link, &req_word_order));
1449
1450   if (mp_op != MP_CopMpByteOrderRequest || dtag != MP_MpDict) {
1451      MP_LogEvent(link, MP_ERROR_EVENT, 
1452          "Problem exchanging initialization information: byte order");
1453      return MP_Failure;
1454   }
1455
1456
1457   /*
1458    * We only have to do something if the word order has changed.
1459    * This will only happen if both parties are Little Endian
1460    */
1461   if (req_word_order == (unsigned long)link->env->native_word_order
1462       && link->env->native_word_order != link->link_word_order) {
1463       link->link_word_order = link->env->native_word_order;
1464       MP_LogEvent(link, MP_INIT_EVENT,
1465                   "Link word order changed to Little Endian");
1466   }
1467
1468   return MP_Success;
1469}
1470
1471
1472
1473#ifdef __STDC__
1474static MP_Status_t tcp_negotiate_fp_format(MP_Link_pt link)
1475#else
1476static MP_Status_t tcp_negotiate_fp_format(link)
1477    MP_Link_pt link;
1478#endif
1479{
1480    MP_DictTag_t   dtag;
1481    MP_NumAnnot_t  na;
1482    MP_NumChild_t  nc;
1483    unsigned long  req_fp_format;
1484    MP_Common_t    mp_op;
1485
1486
1487#ifdef MP_DEBUG
1488    printf("tcp_negotiate_fp_format: entering\n");
1489#endif
1490
1491    /*
1492     * Step 1:  We send our fp format preference to our partner
1493     */
1494    ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1495                                       MP_CopMpFpFormatRequest, 0, 1));
1496    ERR_CHK(IMP_PutUint32(link, (unsigned long)link->env->native_fp_format));
1497    MP_EndMsgReset(link);
1498
1499    /*
1500     * Step 2: Get a reply.  It should be "MP_FpFormatRequest"
1501     * probably should peek at the data stream here to be sure we are
1502     *  getting the right thing
1503     */
1504    ERR_CHK(MP_SkipMsg(link));
1505    ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1506    ERR_CHK(IMP_GetUint32(link, &req_fp_format));
1507
1508   if (mp_op != MP_CopMpFpFormatRequest || dtag != MP_MpDict) {
1509      MP_LogEvent(link, MP_ERROR_EVENT, 
1510          "Problem exchanging initialization information: fp format");
1511      return MP_Failure;
1512   }
1513
1514    /*
1515     * We only have to do something if the fp format has changed.
1516     * This will only happen if both parties are something other than IEEE.
1517     */
1518    if (req_fp_format == (unsigned long)link->env->native_fp_format
1519        && link->env->native_fp_format != link->link_fp_format) {
1520        link->link_fp_format = link->env->native_fp_format;
1521        MP_LogEvent(link, MP_INIT_EVENT,
1522                    "Link floating point format changed to VAX");
1523    }
1524
1525#ifdef MP_DEBUG
1526    printf("tcp_negotiate_fp_format: exiting\n");
1527#endif
1528
1529    return MP_Success;
1530}
1531
1532
1533
1534#ifdef __STDC__
1535static MP_Status_t tcp_negotiate_bigint_format(MP_Link_pt link)
1536#else
1537static MP_Status_t tcp_negotiate_bigint_format(link)
1538    MP_Link_pt link;
1539#endif
1540{
1541    MP_DictTag_t   dtag;
1542    MP_NumAnnot_t  na;
1543    MP_NumChild_t  nc;
1544    unsigned long  req_bigint_format;
1545    MP_Common_t    mp_op;
1546
1547#ifdef MP_DEBUG
1548    printf("tcp_negotiate_bigint_format: entering\n");
1549#endif
1550
1551    /*
1552     * Step 1:  We send our bigint format preference to our partner
1553     */
1554    ERR_CHK(MP_PutCommonOperatorPacket(link, MP_MpDict, 
1555                                       MP_CopMpBigIntFormatRequest, 0, 1));
1556    ERR_CHK(IMP_PutUint32(link, (unsigned long)link->link_bigint_format));
1557    MP_EndMsgReset(link);
1558
1559    /*
1560     * Step 2: Get a reply.  It should be "MP_BigIntFormatRequest"
1561     * probably should peek at the data stream here to be sure we are
1562     * getting the right thing
1563     */
1564    ERR_CHK(MP_SkipMsg(link));
1565    ERR_CHK(MP_GetCommonOperatorPacket(link, &dtag, &mp_op, &na, &nc));
1566    ERR_CHK(IMP_GetUint32(link, &req_bigint_format));
1567
1568    if (mp_op != MP_CopMpBigIntFormatRequest || dtag != MP_MpDict) {
1569      MP_LogEvent(link, MP_ERROR_EVENT, 
1570          "Problem exchanging initialization information: big int format");
1571      return MP_Failure;
1572    }
1573
1574    /*
1575     * We only have to do something if both ends are not already the same
1576     */
1577    if (req_bigint_format != (unsigned long)link->link_bigint_format) {
1578        link->link_bigint_format = MP_GMP;
1579        MP_LogEvent(link, MP_INIT_EVENT,
1580                    "Link multiple precision integer format set to GMP");
1581    }
1582
1583#ifdef MP_DEBUG
1584    printf("tcp_negotiate_bigint_format: exiting\n");
1585#endif
1586
1587    return MP_Success;
1588}
1589
1590
1591
1592#ifdef __STDC__
1593MP_Status_t tcp_get_port(SOCKET *sock, short *port)
1594#else
1595MP_Status_t tcp_get_port(sock, port)
1596    SOCKET *s;
1597    short  *port;
1598#endif
1599{
1600    SOCKADDR_IN isin;
1601    int         attempts = 0;
1602
1603    if (*port < IPPORT_RESERVED)
1604        *port = IPPORT_RESERVED;
1605
1606    if ((*sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
1607        return MP_Failure;
1608
1609    /*
1610     * Initialize socket's address structure
1611     */
1612    isin.sin_family      = AF_INET;
1613    isin.sin_addr.s_addr = htonl(INADDR_ANY);
1614    isin.sin_port        = htons(*port);
1615
1616    while (bind(*sock, (SOCKADDR *)&isin, sizeof(isin)) == SOCKET_ERROR) {
1617        if (LASTERROR == ERRORCODE(EADDRINUSE)) {
1618            (*port)++;
1619            /* since port is an unsigned short, it may wrap around */
1620            if (*port < IPPORT_RESERVED)
1621                *port = IPPORT_RESERVED;
1622            isin.sin_port = htons(*port);
1623            attempts = 0;
1624        }
1625
1626        if (attempts++ > MP_MAX_BIND_ATTEMPTS)
1627            return MP_Failure;
1628    }
1629
1630    return MP_Success;
1631}
1632
1633
1634
1635#ifdef __WIN32__
1636/*
1637 * WinSockInitialize calls the WinSock API initialization function WSAStartup
1638 * and returns a value != 0 if something went wrong. Extended error information
1639 * can be retrieved later by calling WSAGetLastError();
1640 */
1641#ifdef __STDC__
1642int WinSockInitialize(void)
1643#else /* __STDC__ */
1644int WinSockInitialize()
1645#endif /* __STDC__ */
1646{
1647   WORD        wVersionRequired;
1648   WSADATA     wsaData;
1649
1650   wVersionRequired = MAKEWORD(1, 1);
1651   return WSAStartup(wVersionRequired, &wsaData);
1652}
1653
1654
1655
1656/*
1657 * splitargs splits a string of words separated by white-space characters into
1658 * multiple strings by inserting Null characters after the ends of the words.
1659 * The function returns the number of words found in the string.
1660 */
1661#ifdef __STDC__
1662static int splitargs(char *argstr)
1663#else /* __STDC__ */
1664static int splitargs(argstr)
1665   char *argstr;
1666#endif /* __STDC__ */
1667{
1668    char *p, *q;
1669    int   argcnt, wordflag, quoted;
1670
1671    p = q = argstr;
1672    argcnt = wordflag = quoted = 0;
1673
1674    /* Step through all characters in the string. */
1675    while (*p)
1676        switch (*p) {
1677            /* Is *p a space or a tab character? */
1678        case ' ':
1679        case '\t':
1680            /* Simply pass it on if it is part of a `quoted` string. */
1681            if (quoted) {
1682                *q++ = *p++;
1683                break;
1684            }
1685
1686            /*
1687             * If the character marks the end of a word then terminate that
1688             * word with a Null character, mark the word as read and increase
1689             * the argument counter.
1690             */
1691            if (wordflag) {
1692                *q++ = '\0';
1693                wordflag = 0;
1694                ++argcnt;
1695            }
1696            p++;
1697            break;
1698
1699            /* Is *p a backquote character? */
1700        case '`':
1701
1702            /* If the next character is another backquote then regard the
1703             * combination of both as a single literal backquote and pass it
1704             * on to the result string. If not, set the quote mode, i.e. pass
1705             * on all following characters until the next backquote is found or
1706             * the end of the string reached.
1707             */
1708            if (*++p == '`') {
1709                *q++ = *p++;
1710                wordflag = 1;
1711            } else
1712                quoted = !quoted;
1713            break;
1714
1715            /* Put all other characters into the result string. */
1716        default:
1717            wordflag = 1;
1718            *q++ = *p++;
1719            break;
1720        }
1721
1722    /* Terminate the last word with a Null character. */
1723    if (wordflag) {
1724        ++argcnt;
1725        *q = '\0';
1726    }
1727
1728    return argcnt;
1729}
1730
1731#endif /* __WIN32__ */
1732
1733
Note: See TracBrowser for help on using the repository browser.