/* $Id: sock.c,v 1.4 1999/06/22 12:23:35 levitte Exp $ */
#include "fish.h"

#include "sock.h"
#include "ssh.h"

#include <socket.h>
#include <in.h>
#include <inet.h>
#include <netdb.h>

long sock_write(unsigned char *data, long amt, void *state, Erf,void*);
void sock_dispatch(unsigned char *data, long amt, void *state, Erf,void*);

void *sock_init(const char *host, int port, int *fd,Erf erf,void* erfp)
{
    sock_state *state;
    struct hostent *hostent;
    struct sockaddr_in sockaddr;
    struct protoent *tcp_proto;

    state = MemPtrNew(sizeof(sock_state));
    if (!state) {
	ssh_F_memfull();
	return NULL;
    }

    state->fd = -1;
    state->up_dispatch = NULL;
    state->up_setconnid = NULL;
    state->up_state = NULL;

    if ((tcp_proto = getprotobyname("tcp")) == NULL)
    {
	perror("fatal error, can't find protocol tcp");
	free(state);
	return NULL;
    }
    
    if ((hostent = gethostbyname(host)) != NULL)
    {
	sockaddr.sin_family = hostent->h_addrtype;
	memcpy(&sockaddr.sin_addr,
	       hostent->h_addr, hostent->h_length);
    }
    else
    {
	sockaddr.sin_family = AF_INET;
	if ((sockaddr.sin_addr.s_addr = inet_addr(host)) == -1)
	{
	    fprintf(stderr, "Can't resolve hostname \"%s\": ", host);
	    perror("");
	    free(state);
	    return NULL;
	}
    }
    
    sockaddr.sin_port = htons(port);

    if ((state->fd = socket(AF_INET, SOCK_STREAM, tcp_proto->p_proto)) == -1)
    {
	perror("socket(target)");
	free(state);
	return NULL;
    }
    
    if (connect(state->fd,
		(struct sockaddr *)&sockaddr,
		sizeof(sockaddr)) == -1)
    {
	perror("connect(target)");
	free(state);
	return NULL;
    }

    if (fd)
	*fd = state->fd;

    return state;
}

void sock_set_dispatch(
    void (*up_dispatch)(unsigned char *,long,void *,Erf,void*),
    void *up_state, void *state_, Erf erf, void *erfp)
{
    sock_state *state = state_;

    state->up_dispatch = up_dispatch;
    state->up_state = up_state;
}

void sock_set_setconnid(void (*up_setconnid)(char *, void *, Erf, void *),
    void *state_, Erf erf, void *erfp)
{
    sock_state *state = state_;

    state->up_setconnid = up_setconnid;
    if (state->up_setconnid) {
	(state->up_setconnid)(state->connid, state->up_state,erf,erfp);
    }
}

long sock_write(unsigned char *data, long len, void *state_,
    Erf erf, void *erfp)
{
    long totret = 0;
    sock_state *state = state_;

    if (len < 0) {
	close(state->fd);

	MemPtrFree(state);
	totret = -1;
	goto sockwrite_out;
    }

    totret = send(state->fd, data, len, 0);

sockwrite_out:
    return totret;
}

/* This function is called when data arrives */
void sock_dispatch(unsigned char *data, long len, void *state_, Erf erf,
    void *erfp)
{
    sock_state *state = state_;

    if (len < 0) {
	/* The connection was closed; pass on the info */
	goto sock_dispatch_out;
    }

    if (state->up_dispatch) {
	(state->up_dispatch)(data, len, state->up_state, erf, erfp);
    }
	    
sock_dispatch_out:
    ;
}


/* Emacs local variables

Local variables:
eval: (set-c-style "BSD")
end:

*/
