mirror of git://git.psyced.org/git/psyclpc
917 lines
24 KiB
C
917 lines
24 KiB
C
/*---------------------------------------------------------------------------
|
|
* XErq - Socket functions.
|
|
* (C) Copyright 1995 by Brian Gerst.
|
|
* (C) Copyright 2001 by Brian Gerst, Frank Kirschner, Lars Duening.
|
|
*---------------------------------------------------------------------------
|
|
* This module implements the communication requests, and also contains
|
|
* the functions handling socket_t structures.
|
|
*---------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "defs.h"
|
|
|
|
#if defined(_AIX)
|
|
typedef unsigned long length_t;
|
|
#elif defined(__INTEL_COMPILER) || defined (__GNUC__)
|
|
typedef socklen_t length_t;
|
|
#else
|
|
typedef int length_t;
|
|
#endif
|
|
|
|
static int send_auth (auth_t *ap);
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
socket_t *
|
|
new_socket(int fd, char type)
|
|
|
|
/* Create a socket_t of <type> for <fd>, assign it a ticket and add it to
|
|
* the socket list.
|
|
* Return the pointer to the socket structure.
|
|
*/
|
|
|
|
{
|
|
socket_t *sp;
|
|
|
|
sp = malloc(sizeof(struct socket_s));
|
|
XPRINTF((stderr, "%s New socket %p: fd %d\n", time_stamp(), sp, fd));
|
|
sp->next = sockets;
|
|
sp->fd = fd;
|
|
sp->type = type;
|
|
sp->ticket.seq = (seq_number+=seq_interval);
|
|
sp->ticket.rnd = get_ticket();
|
|
sp->queue = NULL;
|
|
sockets = sp;
|
|
return sp;
|
|
} /* new_socket() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static void
|
|
free_socket (socket_t *sp)
|
|
|
|
/* The socket in structure <sp> has been closed already, now remove the
|
|
* structure from the socket list and free it and all still pending
|
|
* data.
|
|
*/
|
|
|
|
{
|
|
socket_t **spp;
|
|
equeue_t *qp, *qnext;
|
|
|
|
XPRINTF((stderr, "%s Freeing socket %p\n", time_stamp(), sp));
|
|
/* Remove the socket from the socket */
|
|
for (spp = &sockets; *spp; spp = &(*spp)->next)
|
|
{
|
|
if (*spp != sp)
|
|
continue;
|
|
XPRINTF((stderr, "%s Unlinking socket %p\n", time_stamp(), sp));
|
|
*spp = sp->next;
|
|
break;
|
|
}
|
|
|
|
/* Free all pending queued data */
|
|
for (qp = sp->queue; qp;)
|
|
{
|
|
XPRINTF((stderr, "%s Freeing queue %p\n", time_stamp(), qp));
|
|
qnext = qp->next;
|
|
free(qp);
|
|
qp = qnext;
|
|
}
|
|
|
|
free(sp);
|
|
} /* free_socket() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
add_to_queue (equeue_t **qpp, char *data, int len, int32 hand)
|
|
|
|
/* Add the message <data> of <len> bytes to the end of queue <qpp>.
|
|
*/
|
|
|
|
{
|
|
equeue_t *new;
|
|
|
|
new = malloc(sizeof(struct equeue_s)+len);
|
|
XPRINTF((stderr, "%s New queue %p: data %p:%d, queue head %p (-> %p)\n"
|
|
, time_stamp(), new, data, len, qpp, *qpp));
|
|
new->len = len;
|
|
new->pos = 0;
|
|
new->next = 0;
|
|
new->handle = hand;
|
|
memcpy(&new->buf, data, len);
|
|
while (*qpp)
|
|
qpp = &(*qpp)->next;
|
|
*qpp = new;
|
|
} /* add_to_queue() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
int
|
|
flush_queue (equeue_t **qpp, int fd, int reply_handle)
|
|
|
|
/* Try to write all pending data from queue <qpp> to socket <fd>.
|
|
* Return -1 if an error occured, and 0 if the queue could be emptied.
|
|
* Single queue elements which have been written are always removed.
|
|
*/
|
|
|
|
{
|
|
int l;
|
|
equeue_t *qp = *qpp, *next;
|
|
|
|
XPRINTF((stderr, "%s Flush queue %p (-> %p) to %d\n"
|
|
, time_stamp(), qpp, *qpp, fd));
|
|
do {
|
|
XPRINTF((stderr, "%s Flush queue entry %p, data %p:%d:%d\n"
|
|
, time_stamp(), qp, qp->buf, qp->pos, qp->len));
|
|
do
|
|
l = write(fd, qp->buf+qp->pos, qp->len);
|
|
while (l==-1 && errno==EINTR);
|
|
XPRINTF((stderr, "%s Wrote %d of %d bytes.\n", time_stamp(), l, qp->len));
|
|
if (l < 0)
|
|
return l;
|
|
if (l < qp->len)
|
|
{
|
|
qp->pos += l;
|
|
qp->len -= l;
|
|
break;
|
|
}
|
|
XPRINTF((stderr, "%s Freeing queue entry %p.\n", time_stamp(), qp));
|
|
if (reply_handle) {
|
|
char r_ok[] = { ERQ_OK, 0 };
|
|
reply1(qp->handle, r_ok, sizeof(r_ok));
|
|
}
|
|
next = qp->next;
|
|
free(qp);
|
|
qp = next;
|
|
} while(next);
|
|
|
|
XPRINTF((stderr, "%s Re-setting queue to entry %p.\n", time_stamp(), qp));
|
|
*qpp = qp;
|
|
return 0;
|
|
} /* flush_queue() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
close_socket (socket_t *sp)
|
|
|
|
/* Close the socket associated with <sp> and free the structure and all
|
|
* data. The function takes care that child_t structures referencing this
|
|
* socket are updated as well.
|
|
* Before the socket is closed, the proper message is sent back to
|
|
* the driver.
|
|
*/
|
|
|
|
{
|
|
child_t *chp;
|
|
socket_t *stmp;
|
|
int found = 0;
|
|
|
|
XPRINTF((stderr, "%s Closing socket %p\n", time_stamp(), sp));
|
|
|
|
/* Check if the socket is still active */
|
|
for (stmp = sockets; stmp; stmp = stmp->next)
|
|
if (stmp == sp)
|
|
{
|
|
found = 1;
|
|
break;
|
|
}
|
|
|
|
if (!found) /* socket already closed */
|
|
{
|
|
XPRINTF((stderr, "%s Socket %p already closed.\n", time_stamp(), sp));
|
|
return;
|
|
}
|
|
|
|
/* Depending on the socket type, handle the fluff
|
|
* depending on the socket.
|
|
*/
|
|
switch(sp->type)
|
|
{
|
|
case SOCKET_STDOUT:
|
|
{
|
|
for (chp = childs; chp; chp=chp->next)
|
|
if (chp->fd == sp)
|
|
break;
|
|
if (chp)
|
|
{
|
|
XPRINTF((stderr, "%s Remove 'fd' link from child %p.\n"
|
|
, time_stamp(), chp));
|
|
chp->fd = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SOCKET_STDERR:
|
|
{
|
|
for (chp = childs; chp; chp=chp->next)
|
|
if (chp->err == sp)
|
|
break;
|
|
if (chp)
|
|
{
|
|
XPRINTF((stderr, "%s Remove 'err' link from child %p.\n"
|
|
, time_stamp(), chp));
|
|
chp->err = NULL;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SOCKET_WAIT_AUTH:
|
|
case SOCKET_AUTH:
|
|
{
|
|
auth_t *ap = (auth_t *)sp;
|
|
XPRINTF((stderr, "%s Write auth info %p:%d\n"
|
|
, time_stamp(), ap->buf, ap->pos));
|
|
write_32(ap->buf, ap->pos);
|
|
write_32((ap->buf)+4, ap->s.handle);
|
|
write1(ap->buf, ap->pos);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
char r[] = { ERQ_EXITED, 0 };
|
|
reply1(sp->handle, r, sizeof(r));
|
|
}
|
|
}
|
|
|
|
close(sp->fd);
|
|
free_socket(sp);
|
|
} /* close_socket() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
int
|
|
read_socket (socket_t *sp, int rw)
|
|
|
|
/* The socket <sp> was flagged by select() as ready for reading (<rw> == 0)
|
|
* resp. writing (<rw> == 1). According to the type of socket take the
|
|
* appropriate action.
|
|
*
|
|
* If there is data pending on the socket, try to write it in any case.
|
|
*
|
|
* Return 0 on success, 1 if an error occured and the socket has been closed.
|
|
*/
|
|
|
|
{
|
|
char buf[ERQ_MAX_REPLY];
|
|
int num;
|
|
|
|
num = 0;
|
|
|
|
XPRINTF((stderr, "%s Read socket %p (fd %d, type %d) , rw %d\n"
|
|
, time_stamp(), sp, sp->fd, sp->type, rw));
|
|
|
|
if (sp->queue)
|
|
flush_queue(&sp->queue, sp->fd, 1);
|
|
|
|
switch(sp->type)
|
|
{
|
|
case SOCKET_LISTEN:
|
|
{
|
|
/* There is a connection pending */
|
|
char r[] = { ERQ_STDOUT };
|
|
|
|
reply1keep(sp->handle, r, sizeof(r));
|
|
sp->type = SOCKET_WAIT_ACCEPT;
|
|
XPRINTF((stderr, "%s Signaling driver to accept connction\n"
|
|
, time_stamp() ));
|
|
return 0;
|
|
}
|
|
|
|
case SOCKET_WAIT_ACCEPT:
|
|
return 0;
|
|
|
|
case SOCKET_UDP:
|
|
{
|
|
/* Read the data message and pass it on */
|
|
|
|
struct sockaddr_in addr;
|
|
length_t len;
|
|
|
|
do {
|
|
char r[] = { ERQ_STDOUT };
|
|
|
|
len = sizeof(addr);
|
|
num = recvfrom(sp->fd, buf, ERQ_MAX_REPLY-20, 0,
|
|
(struct sockaddr *)&addr, &len);
|
|
if (num < 0 && errno==EINTR)
|
|
continue;
|
|
{ int myerrno = errno;
|
|
XPRINTF((stderr, "%s Received UDP: %d bytes of %d.\n"
|
|
, time_stamp(), num, len));
|
|
errno = myerrno;
|
|
}
|
|
if (num <= 0)
|
|
break;
|
|
replyn(sp->handle, 1, 4,
|
|
r, sizeof(r),
|
|
(char *) &addr.sin_addr.s_addr, 4,
|
|
(char *) &addr.sin_port, 2,
|
|
buf, num);
|
|
} while(1);
|
|
break;
|
|
}
|
|
|
|
case SOCKET_WAIT_CONNECT:
|
|
{
|
|
if (rw)
|
|
{
|
|
/* Our opened TCP socket finally connected */
|
|
|
|
char r[] = { ERQ_OK };
|
|
|
|
XPRINTF((stderr, "%s Socket connected.\n"
|
|
, time_stamp() ));
|
|
sp->type = SOCKET_CONNECTED;
|
|
replyn(sp->handle, 1, 2,
|
|
r, sizeof(r),
|
|
(char *) &sp->ticket, TICKET_SIZE);
|
|
return 0;
|
|
}
|
|
XPRINTF((stderr, "%s Socket not connected yet.\n"
|
|
, time_stamp() ));
|
|
/* FALLTHROUGH */
|
|
}
|
|
|
|
case SOCKET_STDOUT:
|
|
case SOCKET_STDERR:
|
|
case SOCKET_CONNECTED:
|
|
{
|
|
/* Read data from the stream and pass it on */
|
|
|
|
char r_err[] = { ERQ_STDERR };
|
|
char r_out[] = { ERQ_STDOUT };
|
|
|
|
XPRINTF((stderr, "%s Attempting to read data from socket.\n"
|
|
, time_stamp() ));
|
|
|
|
do {
|
|
num = read(sp->fd, buf, ERQ_MAX_REPLY-14);
|
|
if (num < 0 && errno == EINTR)
|
|
continue;
|
|
{ int myerrno = errno;
|
|
XPRINTF((stderr, "%s Received stream: %d bytes.\n"
|
|
, time_stamp(), num));
|
|
errno = myerrno;
|
|
}
|
|
if (num <= 0)
|
|
break;
|
|
replyn(sp->handle, 1, 2,
|
|
(sp->type==SOCKET_STDERR) ? r_err : r_out, 1,
|
|
buf, num);
|
|
} while(1);
|
|
break;
|
|
}
|
|
|
|
case SOCKET_WAIT_AUTH:
|
|
{
|
|
if (rw)
|
|
{
|
|
/* Our authd connection is finally there */
|
|
|
|
sp->type = SOCKET_AUTH;
|
|
return send_auth((auth_t *)sp);
|
|
}
|
|
/* FALLTHROUGH */
|
|
}
|
|
|
|
case SOCKET_AUTH:
|
|
{
|
|
auth_t *ap = (struct auth_s *)sp;
|
|
do {
|
|
/* Tell the driver what we got from the authd */
|
|
|
|
num = read(ap->s.fd, &ap->buf[ap->pos], sizeof(ap->buf)-ap->pos);
|
|
if (num < 0 && errno == EINTR)
|
|
continue;
|
|
{ int myerrno = errno;
|
|
XPRINTF((stderr, "%s Received auth: %d bytes.\n"
|
|
, time_stamp(), num));
|
|
errno = myerrno;
|
|
}
|
|
if (num <= 0)
|
|
{
|
|
close_socket(sp);
|
|
return 1;
|
|
}
|
|
ap->pos += num;
|
|
} while(1);
|
|
}
|
|
} /* switch(socket->type) */
|
|
|
|
if (!num)
|
|
{
|
|
/* We got EOF when reading data - the socket is no longer needed.
|
|
*/
|
|
|
|
XPRINTF((stderr, "%s EOF when reading data.\n", time_stamp()));
|
|
close_socket(sp);
|
|
return 1;
|
|
}
|
|
|
|
if (errno != EWOULDBLOCK
|
|
#if EWOULDBLOCK!=EAGAIN
|
|
&& errno != EAGAIN
|
|
#endif
|
|
)
|
|
{
|
|
int myerrno = errno;
|
|
XPRINTF((stderr, "%s Error %d when reading data.\n", time_stamp(), errno));
|
|
errno = myerrno;
|
|
reply_errno(sp->handle);
|
|
close_socket(sp);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
} /* read_socket() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
erq_send (char *mesg, int msglen)
|
|
|
|
/* ERQ_SEND: send data to the socket identified by the ticket.
|
|
*/
|
|
|
|
{
|
|
ticket_t *ticket;
|
|
socket_t *sp;
|
|
int num;
|
|
char *data;
|
|
|
|
/* Find the socket identified in the message */
|
|
|
|
if (msglen < 9+TICKET_SIZE)
|
|
{
|
|
char r_arglen[] = { ERQ_E_ARGLENGTH };
|
|
reply1(get_handle(mesg), r_arglen, sizeof(r_arglen));
|
|
return;
|
|
}
|
|
|
|
ticket = (struct ticket_s *) (mesg+9);
|
|
data = mesg+9+TICKET_SIZE;
|
|
msglen -= (TICKET_SIZE+9);
|
|
|
|
for (sp = sockets; sp; sp = sp->next)
|
|
if (!memcmp(ticket, &sp->ticket, TICKET_SIZE))
|
|
break;
|
|
|
|
if (!sp)
|
|
{
|
|
char r_ticket[] = { ERQ_E_TICKET };
|
|
reply1(get_handle(mesg), r_ticket, sizeof(r_ticket));
|
|
return;
|
|
}
|
|
|
|
XPRINTF((stderr, "%s Send %d bytes to socket %p, type %d\n"
|
|
, time_stamp(), msglen, sp, sp->type));
|
|
|
|
/* Act according to the type of the socket */
|
|
switch(sp->type)
|
|
{
|
|
case SOCKET_STDOUT:
|
|
case SOCKET_CONNECTED:
|
|
{
|
|
/* Send the data to the stream */
|
|
|
|
do
|
|
num = write(sp->fd, data, msglen);
|
|
while (num == -1 && errno == EINTR);
|
|
{ int myerrno = errno;
|
|
XPRINTF((stderr, "%s Stream: Wrote %d bytes of %d.\n"
|
|
, time_stamp(), num, msglen));
|
|
errno = myerrno;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SOCKET_UDP:
|
|
{
|
|
/* Send the message to the specified address */
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
addr.sin_family=AF_INET;
|
|
memcpy(&addr.sin_addr.s_addr, data, 4);
|
|
memcpy(&addr.sin_port, data+4, 2);
|
|
data += 6;
|
|
msglen -= 6;
|
|
do
|
|
num = sendto(sp->fd, data, msglen, 0, (struct sockaddr *)&addr,
|
|
sizeof(addr));
|
|
while(num == -1 && errno == EINTR);
|
|
{ int myerrno = errno;
|
|
XPRINTF((stderr, "%s UDP: Wrote %d bytes of %d.\n"
|
|
, time_stamp(), num, msglen));
|
|
errno = myerrno;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SOCKET_WAIT_ACCEPT:
|
|
{
|
|
/* Default to accepting the socket. */
|
|
erq_accept(mesg, msglen);
|
|
return;
|
|
}
|
|
|
|
default:
|
|
{
|
|
char r_ticket[] = { ERQ_E_TICKET };
|
|
reply1(get_handle(mesg), r_ticket, sizeof(r_ticket));
|
|
return;
|
|
}
|
|
} /* switch(socket->type) */
|
|
|
|
/* Return the success of the action */
|
|
|
|
if (num == msglen)
|
|
{
|
|
char r_ok[] = { ERQ_OK, 0 };
|
|
reply1(get_handle(mesg), r_ok, sizeof(r_ok));
|
|
return;
|
|
}
|
|
else if (num >= 0)
|
|
{
|
|
XPRINTF((stderr, "%s Queue remaining data: %p:%d\n"
|
|
, time_stamp(), mesg+num, msglen-num));
|
|
add_to_queue(&sp->queue, data+num, msglen-num, get_handle(mesg));
|
|
num=htonl(num);
|
|
replyn(get_handle(mesg), 1, 2,
|
|
(char[]) { ERQ_E_INCOMPLETE }, 1,
|
|
(char *)&num, 4);
|
|
return;
|
|
}
|
|
|
|
reply_errno(get_handle(mesg));
|
|
if ( errno != EWOULDBLOCK
|
|
#if EWOULDBLOCK!=EAGAIN
|
|
&& errno != EAGAIN
|
|
#endif
|
|
)
|
|
{
|
|
XPRINTF((stderr, "%s Error %d when writing data.\n"
|
|
, time_stamp(), errno));
|
|
close_socket(sp);
|
|
}
|
|
} /* erq_send() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
erq_listen (char *mesg, int msglen)
|
|
|
|
/* ERQ_LISTEN: Open a socket to listen for connections.
|
|
*/
|
|
|
|
{
|
|
struct sockaddr_in addr;
|
|
socket_t *sp;
|
|
int fd, tmp;
|
|
|
|
/* Check if the message is ok */
|
|
if (msglen != 11)
|
|
{
|
|
char r_arglen[] = { ERQ_E_ARGLENGTH, 0 };
|
|
reply1(get_handle(mesg), r_arglen, sizeof(r_arglen));
|
|
return;
|
|
}
|
|
|
|
/* Get the port number */
|
|
addr.sin_addr.s_addr=INADDR_ANY;
|
|
addr.sin_family=AF_INET;
|
|
memcpy(&addr.sin_port, mesg + 9, 2);
|
|
|
|
/* Open and initialise the socket */
|
|
tmp=((fd=socket(AF_INET, SOCK_STREAM, 0)) < 0);
|
|
tmp=tmp || (fcntl(fd, F_SETFD, 1) < 0);
|
|
tmp=tmp || (fcntl(fd, F_SETFL, O_NONBLOCK) < 0);
|
|
tmp=tmp || (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&msglen,
|
|
sizeof(msglen)) < 0);
|
|
tmp=tmp || (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0);
|
|
tmp=tmp || (listen(fd, 5) < 0);
|
|
if (tmp)
|
|
{
|
|
if (fd >= 0)
|
|
close(fd);
|
|
reply_errno(get_handle(mesg));
|
|
return;
|
|
}
|
|
|
|
/* Make the new socket structure */
|
|
sp = new_socket(fd, SOCKET_LISTEN);
|
|
sp->handle = get_handle(mesg);
|
|
|
|
{
|
|
char r_ok[] = { ERQ_OK };
|
|
replyn(sp->handle, 1, 2,
|
|
r_ok, sizeof(r_ok),
|
|
(char *) &sp->ticket, TICKET_SIZE);
|
|
}
|
|
} /* erq_listen() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
erq_open_udp (char *mesg, int msglen)
|
|
|
|
/* ERQ_OPEN_UDP: Open an UDP socket.
|
|
*/
|
|
|
|
{
|
|
struct sockaddr_in addr;
|
|
socket_t *sp;
|
|
int fd, tmp;
|
|
|
|
if (msglen != 11)
|
|
{
|
|
char r_arglen[] = { ERQ_E_ARGLENGTH, 0 };
|
|
reply1(get_handle(mesg), r_arglen, sizeof(r_arglen));
|
|
return;
|
|
}
|
|
|
|
/* Get the port to use */
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
addr.sin_family = AF_INET;
|
|
memcpy(&addr.sin_port, mesg + 9, 2);
|
|
|
|
/* Open and initialise the socket */
|
|
|
|
if ((fd=socket(AF_INET, SOCK_DGRAM, 0)) < 0
|
|
|| fcntl(fd, F_SETFD, 1) < 0
|
|
|| fcntl(fd, F_SETFL, O_NONBLOCK) < 0
|
|
|| setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&tmp,
|
|
sizeof(tmp)) < 0
|
|
|| bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
|
|
{
|
|
if (fd >= 0)
|
|
close(fd);
|
|
reply_errno(get_handle(mesg));
|
|
return;
|
|
}
|
|
|
|
/* Create the new socket structure */
|
|
sp = new_socket(fd, SOCKET_UDP);
|
|
sp->handle = get_handle(mesg);
|
|
|
|
{
|
|
char r_ok[] = { ERQ_OK };
|
|
replyn(sp->handle, 1, 2,
|
|
r_ok, sizeof(r_ok),
|
|
(char *) &sp->ticket, TICKET_SIZE);
|
|
}
|
|
return;
|
|
} /* erq_open_udp() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
erq_open_tcp (char *mesg, int msglen)
|
|
|
|
/* ERQ_OPEN_TCP: Open a TCP socket and connect it to a given address.
|
|
* If the connection can't be completed immediately (EINPROGRESS), the
|
|
* socket is set to SOCKET_WAIT_CONNECT.
|
|
*/
|
|
|
|
{
|
|
struct sockaddr_in addr;
|
|
socket_t *sp;
|
|
int fd, tmp, status;
|
|
|
|
if (msglen != 15)
|
|
{
|
|
char r_arglen[] = { ERQ_E_ARGLENGTH, 0 };
|
|
reply1(get_handle(mesg), r_arglen, sizeof(r_arglen));
|
|
return;
|
|
}
|
|
|
|
/* Get the address */
|
|
memcpy(&addr.sin_addr.s_addr, mesg + 9, 4);
|
|
addr.sin_family=AF_INET;
|
|
memcpy(&addr.sin_port, mesg + 13, 2);
|
|
status=SOCKET_CONNECTED;
|
|
|
|
/* Initialize the socket */
|
|
if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0
|
|
|| fcntl(fd, F_SETFD, 1) < 0
|
|
|| fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
|
{
|
|
if (fd >= 0)
|
|
close(fd);
|
|
reply_errno(get_handle(mesg));
|
|
return;
|
|
}
|
|
|
|
/* Try to connect */
|
|
do
|
|
tmp=connect(fd, (struct sockaddr *) &addr, sizeof(addr));
|
|
while (tmp && errno==EINTR);
|
|
|
|
if (!tmp)
|
|
status = SOCKET_CONNECTED;
|
|
else if (errno == EINPROGRESS)
|
|
{
|
|
status = SOCKET_WAIT_CONNECT;
|
|
tmp = 0;
|
|
}
|
|
else
|
|
{
|
|
if (fd >= 0)
|
|
close(fd);
|
|
reply_errno(get_handle(mesg));
|
|
return;
|
|
}
|
|
|
|
/* Create the new socket structure */
|
|
sp = new_socket(fd, status);
|
|
sp->handle = get_handle(mesg);
|
|
if (status == SOCKET_WAIT_CONNECT)
|
|
return;
|
|
|
|
{
|
|
char r_ok[] = { ERQ_OK };
|
|
replyn(sp->handle, 1, 2,
|
|
r_ok, sizeof(r_ok),
|
|
(char *) &sp->ticket, TICKET_SIZE);
|
|
}
|
|
return;
|
|
} /* erq_open_tcp() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
erq_accept (char *mesg, int msglen)
|
|
|
|
/* ERQ_ACCEPT: Accept a new, pending connection.
|
|
*/
|
|
|
|
{
|
|
struct sockaddr_in addr;
|
|
socket_t *sp;
|
|
int fd;
|
|
length_t tmp;
|
|
|
|
if (msglen != 9+TICKET_SIZE)
|
|
{
|
|
char r_arglen[] = { ERQ_E_ARGLENGTH, 0 };
|
|
reply1(get_handle(mesg), r_arglen, sizeof(r_arglen));
|
|
return;
|
|
}
|
|
|
|
/* Find the socket we're accepting on */
|
|
for (sp = sockets; sp; sp = sp->next)
|
|
if (!memcmp(mesg+9, &sp->ticket, TICKET_SIZE))
|
|
break;
|
|
|
|
/* Is there really something pending? */
|
|
if (sp->type != SOCKET_WAIT_ACCEPT)
|
|
{
|
|
char r_ticket[] = { ERQ_E_TICKET };
|
|
reply1(get_handle(mesg), r_ticket, sizeof(r_ticket));
|
|
return;
|
|
}
|
|
|
|
/* Yes, mark the socket as available again */
|
|
sp->type = SOCKET_LISTEN;
|
|
|
|
/* Accept and initialize the socket */
|
|
tmp = sizeof(addr);
|
|
if ((fd=accept(sp->fd, (struct sockaddr *)&addr, &tmp)) < 0
|
|
|| fcntl(fd, F_SETFD, 1) < 0
|
|
|| fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
|
|
{
|
|
if (fd >= 0)
|
|
close(fd);
|
|
reply_errno(get_handle(mesg));
|
|
return;
|
|
}
|
|
|
|
/* Create the socket */
|
|
sp = new_socket(fd, SOCKET_CONNECTED);
|
|
sp->handle = get_handle(mesg);
|
|
|
|
{
|
|
char r_ok[] = { ERQ_OK };
|
|
replyn(sp->handle = get_handle(mesg), 1, 4,
|
|
r_ok, sizeof(r_ok),
|
|
(char *) &addr.sin_addr.s_addr, 4,
|
|
(char *) &addr.sin_port, 2,
|
|
(char *) &sp->ticket, TICKET_SIZE);
|
|
}
|
|
} /* erq_accept() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
static int
|
|
send_auth (auth_t *ap)
|
|
|
|
/* We got connection to the authd - send our request.
|
|
* Return 0 on success, 1 if an error occured and the socket has been closed.
|
|
*/
|
|
|
|
{
|
|
char req[100];
|
|
int num;
|
|
|
|
sprintf(req, "%ld,%ld\n", ap->remote_port, ap->local_port);
|
|
do
|
|
num = write(ap->s.fd, req, strlen(req));
|
|
while(num == -1 && errno == EINTR);
|
|
if (num <= 0)
|
|
{
|
|
close_socket((socket_t *)ap);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
} /* send_auth() */
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
void
|
|
erq_auth (char *mesg, int msglen)
|
|
|
|
/* ERQ_AUTH: Connect to a remote authd and request authentification.
|
|
* If the connection can't be completed immediately (EINPROGRESS), the
|
|
* socket is set to SOCKET_WAIT_AUTH.
|
|
*/
|
|
|
|
{
|
|
struct sockaddr_in addr;
|
|
auth_t *ap;
|
|
int fd, tmp, status, local_port, remote_port;
|
|
|
|
switch(msglen) {
|
|
case 17:
|
|
{
|
|
/* The xerq way */
|
|
unsigned short sh;
|
|
|
|
memcpy(&sh, mesg+13, sizeof(sh)); remote_port = ntohs(sh);
|
|
memcpy(&sh, mesg+15, sizeof(sh)); local_port = ntohs(sh);
|
|
|
|
memcpy(&addr.sin_addr.s_addr, mesg+9, 4);
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(AUTH_PORT);
|
|
break;
|
|
}
|
|
|
|
case 13+sizeof(addr):
|
|
{
|
|
/* The standard erq way */
|
|
memcpy(&addr, mesg+9, sizeof(addr));
|
|
remote_port = ntohs(addr.sin_port);
|
|
local_port = read_32(mesg+sizeof(addr)+9);
|
|
addr.sin_port = htons(AUTH_PORT);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
reply1(get_handle(mesg), "", 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Create and initialize the socket */
|
|
if ((fd=socket(AF_INET, SOCK_STREAM, 0))<0
|
|
|| fcntl(fd, F_SETFD, 1)<0
|
|
|| fcntl(fd, F_SETFL, O_NONBLOCK)<0)
|
|
{
|
|
if (fd >= 0)
|
|
close(fd);
|
|
reply1(get_handle(mesg), "", 0);
|
|
return;
|
|
}
|
|
|
|
/* Try to connect to the authd */
|
|
do
|
|
tmp = connect(fd, (struct sockaddr *) &addr, sizeof(addr));
|
|
while(tmp && errno==EINTR);
|
|
|
|
if (!tmp)
|
|
status = SOCKET_AUTH;
|
|
else if (errno == EINPROGRESS)
|
|
status = SOCKET_WAIT_AUTH;
|
|
else
|
|
{
|
|
tmp = errno;
|
|
if (fd >= 0)
|
|
close(fd);
|
|
reply1(get_handle(mesg), "", 0);
|
|
return;
|
|
}
|
|
|
|
/* Create the auth_t structure and put it into the list of sockets */
|
|
ap = malloc(sizeof(struct auth_s));
|
|
ap->s.next = sockets;
|
|
ap->s.fd = fd;
|
|
ap->s.type = status;
|
|
ap->s.ticket.seq = (seq_number+=seq_number);
|
|
ap->s.ticket.rnd = get_ticket();
|
|
ap->s.handle = get_handle(mesg);
|
|
ap->s.queue = NULL;
|
|
ap->local_port = local_port;
|
|
ap->remote_port = remote_port;
|
|
ap->pos = 8;
|
|
sockets = (struct socket_s *)ap;
|
|
|
|
/* If we already got connection, send our request */
|
|
if (status == SOCKET_AUTH)
|
|
send_auth(ap);
|
|
} /* erq_auth() */
|
|
|
|
/***************************************************************************/
|
|
|