RCS_ID_C "$Id: rcmd.c,v 1.6 1993/10/08 00:02:44 ppessi Exp $";
/*
 * rcmd.c --- remote login connection 
 *
 * Author: ppessi <Pekka.Pessi@hut.fi>
 *
 * Copyright (c) 1993 AmiTCP/IP Group, <amitcp-group@hut.fi>
 *                    Helsinki University of Technology, Finland.
 *                    All rights reserved.
 *
 * Created      : Tue May 18 01:59:25 1993 ppessi
 * Last modified: Wed Sep 22 19:57:07 1993 ppessi
 *
 * $Log: rcmd.c,v $
 * Revision 1.6  1993/10/08  00:02:44  ppessi
 * Fixed OOB handling when input buffer overflows.
 *
 * Revision 1.4  1993/08/12  06:19:13  jraja
 * Updated the email-address.
 *
 */

#include <stdio.h>
#include <string.h>
#include "nifty.h"
#include "amiga.h"
#include "nio.h"
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>

struct Library *SocketBase;

#if __SASC
#include <proto/socket.h>
#elif __GNUC__
#include <inline/socket.h>
#else
#include <clib/socket_protos.h>
#endif
#include "rlogin.h"

#define BUFSIZE 256

char *username = NULL;

int errno;

BYTE IO_bit = -1;
BYTE URG_bit = -1;
ULONG SIGURG = 0;

char okwinch = 0;

struct winsize_packet winsize =
{ 0377, 0377, 's', 's', 0, 0, 0, 0 };


long rresvport(long *alport);

void 
CloseSocketBase(void)
{
  if (SocketBase) CloseLibrary(SocketBase), SocketBase = NULL;
  if (IO_bit != -1) FreeSignal(IO_bit), IO_bit = -1;
  if (URG_bit != -1) FreeSignal(URG_bit), URG_bit = -1;
}

void
rsendwsize(int s) 
{
    send(s, &winsize, sizeof(winsize), 0);
}

int 
rread(long s, char *buf, int length)
{
    char tiop;
    int n = 0, m; 
    int wasting = 0;
    char waste[BUFSIZE];

    for (;;) {
	/* We are at OOB data? */
	ioctl(s, SIOCATMARK, &m);
	if (m) {
	    m = recv(s, &tiop, 1, MSG_OOB);
	    if (m < 0) break;

	    if (tiop & TIOCPKT_WINDOW) {
		/* Let server know about window size changes */
		okwinch = 1;	
		rsendwsize(s);
	    }
	    /* Here we might want to handle other bits */
	    if (tiop & TIOCPKT_FLUSHWRITE) {
		for (;;) {
		    if (ioctl(s, SIOCATMARK, &m) < 0) {
			(void)perror(PROGNAME ": ioctl");
			n = -1;
			break;
		    }
		    if (m) break;
		    n = recv(s, waste, sizeof (waste), 0);
		    if (n <= 0) break;
		}
		n = 0;
	    }

	    /* SIGIO is not sent without this */
#if USE_FIONBIO
	    recv(s, waste, sizeof(waste), 0);
#else
	    recv(s, waste, 0, 0);
#endif
	    return n;
	}
	if ((m = recv(s, buf, length, 0)) < 0) 
	    break;
	if (wasting) 
	    continue;
	n += m; buf +=m; length -= m;
	if (length > 0) continue;
	wasting = 1; buf = waste; length = sizeof(waste); 
    } 

    return n;
}

/* 
 * rcmd
 *     Connect to the server using a privileged port
 */
int 
rcmd(char **host, u_short rport, 
     const char *locuser, const char *remuser, const char *term)
{
    int s;
    struct sockaddr_in sin;
    struct in_addr saddr;
    char c;
    struct hostent *hp;
    int lport = IPPORT_RESERVED - 1;
    struct Task *pid = FindTask(NULL);

    sin.sin_len = sizeof(sin);
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = inet_addr(*host);
    if (sin.sin_addr.s_addr == (u_int)-1) {
	hp = gethostbyname(*host);
	if (hp == 0) {
	    fputs(*host, stderr);
	    fputs(": unknow target.\n", stderr);
	    return (-1);
	}
	sin.sin_family = hp->h_addrtype;
	bcopy(hp->h_addr_list[0], (caddr_t)&sin.sin_addr, hp->h_length);
    }
    sin.sin_port = rport;

    /* get a socket with a privileged port */
    s = rresvport(&lport);
    if (s < 0) {
	perror("rcmd: socket");
	return (-1);
    }

    ioctl(s, FIOSETOWN, (char*)&pid);

    if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
	perror(*host);
	return (-1);
    }

    send(s, "", 1, 0);
    send(s, locuser, strlen(locuser)+1, 0);
    send(s, remuser, strlen(remuser)+1, 0);
    send(s, term, strlen(term)+1, 0);
    if (recv(s, &c, 1, 0) != 1) {
	perror(*host);
	goto bad;
    }
    if (c != 0) {
	while (recv(s, &c, 1, 0) == 1) {
	    (void) fputc(c, stderr);
	    if (c == '\n')
		break;
	}
	goto bad;
    }
    return s;
 bad:
    (void) CloseSocket(s);
    return (-1);
}

/*
 * Bind a socket with privileged port
 */
long
rresvport(long *alport)
{
  struct sockaddr_in sin;
  int s;

  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr = INADDR_ANY;
  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s < 0)
    return (-1);
  for (;;) {
    sin.sin_port = htons((u_short)*alport);
    if (bind(s, (struct sockaddr *)&sin, sizeof (sin)) >= 0)
      return (s);
    if (errno != EADDRINUSE) {
      (void) close(s);
      return (-1);
    }
    (*alport)--;
    if (*alport == IPPORT_RESERVED/2) {
      (void) close(s);
      errno = EAGAIN;		/* close */
      return (-1);
    }
  }
}
