source: git/MP/MP/MP_TcpTransp.c @ c23f3f

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