/*++

Copyright (c) 1991  Microsoft Corporation

Module Name:

    rexec.c

Abstract:

    This module implements a routine to transmit commands to a remote
    computer. This routine is used by the rexec program.

Author:

    Mike Massa (mikemas)           Sept 20, 1991

Revision History:

    Who         When        What
    --------    --------    ----------------------------------------------
    mikemas     9-20-91     created
    MuraliK     10-19-94    Fixing localization bug
    MuraliK     06-27-95    on failure to connect it was sleeping for long time
                       (due to 1000*1000 millisecs time to sleep). Fixed!

Notes:

    Exports:
        rexec()

--*/

#ident "@(#)rexec.c     5.3     3/8/91"

/******************************************************************
 *
 *  TCP/IP Library Utility
 *
 *  Copyright 1990 Spider Systems Limited
 *
 *  REXEC.C
 *
 *  Remote execution library routine
 *
 ******************************************************************/


/*
 *       /usr/projects/tcp/SCCS.rel3/rel/src/lib/net/0/s.rexec.c
 *      @(#)rexec.c     5.3
 *
 *      Last delta created      14:11:32 3/4/91
 *      This file extracted     11:20:29 3/8/91
 *
 *      Modifications:
 *
 *      1 June 1990 (RAE)       Ported from Berkeley Version
 */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */
/**************************************************************************/

#include "local.h"
# include "sockutil.h"
# include "nls.h"

#define bcopy(x,y,z)    strncpy(y,x,z)

#define sleep(timeInMillisec)  Sleep((timeInMillisec)) // pick up windows sleep


extern
void
ruserpass(
    char  *host,
    char **aname,
    char **apass
    );

int     rexecoptions;

/**************************************************************************/
SOCKET
rexec(
    IN  char          **ahost,
    IN  unsigned short  rport,
    IN  char           *name,
    IN  char           *pass,
    IN  char           *cmd,
    OUT SOCKET         *fd2p      OPTIONAL
    )
/**************************************************************************/
{
    struct sockaddr_in sin, sin2, lsin, from;
    struct hostent *hp;
    u_short port;
    SOCKET  s, s3;
    int timo = 1;
    char c;
    unsigned long host_addr;
    struct fd_set  readfds, writefds, exceptfds;

    host_addr = inet_addr(*ahost);
    if (host_addr != INADDR_NONE) {

        /* we have a valid IP address, so we can still continue */
        sin.sin_family = AF_INET;
        memcpy((caddr_t)&sin.sin_addr, &host_addr, 4);

    } else {

        hp = gethostbyname(*ahost);
        if ( hp == NULL ) {
            // fprintf(stderr, "%s: unknown host\n", *ahost);
            //
            //  NlsEnabled ( MuraliK) 10-19-94
            //
            NlsPutMsg( STDERR, IDS_UNKNOWN_HOST, *ahost);
            return (INVALID_SOCKET);
        }

        *ahost = hp->h_name;
        sin.sin_family = (short) hp->h_addrtype;
        memcpy((caddr_t)&sin.sin_addr, hp->h_addr, hp->h_length);
    }

    ruserpass(*ahost, &name, &pass);

retry:
    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s == INVALID_SOCKET) {
        perror("rexec: socket");
        return (INVALID_SOCKET);
    }

    memset((char *)&lsin, 0, sizeof (lsin));
    lsin.sin_family = (short) sin.sin_family;

    if (bind(s, (struct sockaddr *) &lsin, sizeof (lsin)) < 0) {
        if (GetLastError() == WSAENETDOWN) {
            // fprintf(stderr, "The network is down\n");
            //
            //  Nls Enabled ( MuraliK)  10-19-94
            //
            NlsPutMsg( STDERR, IDS_NETWORK_IS_DOWN);
        } else {
            // perror("rexec: bind");
            //
            //  Nls Enabled ( MuraliK) 10-19-94
            //
            NlsPerror( IDS_BIND_FAILED, GetLastError() );
        }
        return ( INVALID_SOCKET);
    }

    sin.sin_port = rport;

    if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
        if (GetLastError() == WSAECONNREFUSED && timo <= 16) {
            closesocket(s);
            sleep(timo * 1000);
            timo *= 2;
            goto retry;
        }
        perror("rexec:connect");
        return ( INVALID_SOCKET);
    }

    if (fd2p == 0) {
        (void) send(s, "", 1, 0);
        port = 0;
    } else {
        char num[8];
        SOCKET s2;
        int sin2len;
        
        s2 = socket(AF_INET, SOCK_STREAM, 0);
        if (s2 == INVALID_SOCKET) {
            closesocket(s);
            return (INVALID_SOCKET);
        }

        memset((char *)&sin2, 0, sizeof (sin2));
        sin2.sin_family = (short) sin.sin_family;
        
        if (bind(s2, (struct sockaddr *)&sin2, sizeof (sin2)) < 0) {
            if (GetLastError() == WSAENETDOWN) {
                // fprintf(stderr, "The network is down\n");
                //
                //  Nls Enabled ( MuraliK)  10-19-94
                //
                NlsPutMsg( STDERR, IDS_NETWORK_IS_DOWN);
            } else {
                // perror("rexec: bind");
                //
                //  Nls Enabled ( MuraliK) 10-19-94
                //
                NlsPerror( IDS_BIND_FAILED, GetLastError());
            }
            return ( INVALID_SOCKET);
        }

        listen(s2, 1);

        sin2len = sizeof (sin2);

        if (getsockname(s2, (struct sockaddr *) &sin2, &sin2len) < 0 ||
            sin2len != sizeof (sin2)) {
            perror("rexec: getsockname");
            closesocket(s2);
            goto bad;
        }

        port = (u_short) ntohs( (u_short) sin2.sin_port );
        (void) sprintf(num, "%u", port);
        (void) send(s, num, strlen(num)+1, 0);
        
        {
            int len = sizeof (from);
                
            FD_ZERO( &readfds );
            FD_ZERO( &writefds );
            FD_ZERO( &exceptfds );
            FD_SET( s, &exceptfds );     // check for disconnects
            FD_SET( s2, &readfds );      // check for connects
                
            // wait for accept from remote host or disconect
            // (some machines will just drop the connection if they
            // can't resolve your host name - Suns in particular).
            //
            if (select(2, &readfds, &writefds, &exceptfds, NULL) < 0) {
                errno = GetLastError();
                perror("Select failed");
                closesocket(s2);
                port = 0;
                goto bad;
            }
                
            if ( FD_ISSET( s2, &readfds ) ) {
                // received a connect request from remote host
                
                s3 = accept(s2, (struct sockaddr *)&from, &len);
                closesocket(s2);
                if (s3 == INVALID_SOCKET) {
                    perror("rexec: accept");
                    port = 0;
                    goto bad;
                }
            }
            else {
                if ( FD_ISSET( s, &exceptfds ) ) {
                    // The remote side disconnected before completing
                    // the connect of the second socket.
                    closesocket(s2);
                    port = 0;
                    goto bad;
                }
                else {
                    //
                    // if we ever get here, something is very wrong.
                    //
                    closesocket(s2);
                    port = 0;
                    goto bad;
                }
            }
        }
        *fd2p = s3;
    }
    (void) send(s, name, strlen(name) + 1, 0);

    /*
     * should public key encypt the password here
     */
    (void) send(s, pass, strlen(pass) + 1, 0);
    (void) send(s, cmd, strlen(cmd) + 1, 0);
    if (recv(s, &c, 1, 0) != 1) {
        perror(*ahost);
        goto bad;
    }
    if (c != 0) {
        _write(2, *ahost, strlen(*ahost));
        _write(2, ": ", 2);
        while (recv(s, &c, 1, 0) == 1) {
            (void) _write(2, &c, 1);
            if (c == '\n')
                break;
        }
        goto bad;
    }
    return (s);

bad:
    if (port)
        closesocket(*fd2p);
    closesocket(s);
    return (INVALID_SOCKET);
}