archie/prospero/lib/ardp/ardp_send.c
2024-05-27 16:13:40 +02:00

306 lines
11 KiB
C

/*
* Copyright (c) 1991-1993 by the University of Southern California
*
* Written by bcn 1989-92 as dirsend.c in the Prospero distribution
* Modified by bcn 1/93 separate library and add support for asynchrony
*
* For copying and distribution information, please see the file
* <usc-license.h>.
*/
#include <usc-license.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <ardp.h>
#include <perrno.h>
#include <pmachine.h>
#define OLD_GETHOSTBYNAME /* do it the old way, since we're on the client
*/
#ifdef PROSPERO
#include <pcompat.h>
#else /* not PROSPERO */
#define DISABLE_PFS_START()
#define DISABLE_PFS_END()
#endif /* not PROSPERO */
RREQ ardp_activeQ = NOREQ; /* Info about active requests */
RREQ ardp_completeQ = NOREQ;/* Completed requests */
static unsigned short ardp_def_port_no = 0; /* Default UDP port to use */
int ardp_port = -1; /* Opened UDP port */
extern int pfs_debug; /* Debug level */
static ardp_init();
static short ardp_next_cid();
/*
* ardp_send - send a request and possibly wait for response
*
* ardp_send takes a pointer to a structure of type RREQ, an optional
* hostname, an optional pointer to the desination address, and the time to
* wait before returning in microseconds.
*
* If a destination address was specified, the address is inserted
* into the RREQ structure. If not, but a hostname was specified, the
* hostname is resolved, and its address inserted into the RREQ
* structure. The hostname may be followed by a port number in
* parentheses in which case the port field is filled in. If not
* specified, the Prospero directory server port is used as the default.
* If the host address is a non-null pointer to an empty address, then
* the address is also filled in.
*
* ardp_send then sends the packets specified by the request structure
* to the address in the request structure. If the time to wait is
* -1, it waits until the complete response has been received and
* returns PSUCCESS or PFAILURE depending on the outcome. Any
* returned packets are left in the RREQ strucure. If the time to
* wait is 0, ardp_send returns immediately. The prereq strucure will
* be filled in as the response is received if calls are made to
* ardp_check_messages (which may be called by an interrupt, or
* explicitly at appropriate points in the application. If the time
* to wait is positive, then ardp_send waits the specified lenght of
* time before returning.
*
* If ardp_send returns before the complete response has been
* received, it returns ARDP_PENDING (-1). This means that only
* the status field in the RREQ structure may be used until the status
* field indicates ARDP_STATUS_COMPLETE. In no event shall it be legal
* for the application to modify fields in the RREQ structure while a
* request is pending. If the request completes during the call to
* ardp_send, it returns ARDP_SUCCESS (0). On error, a positive
* return or status value indicates the error that occured.
*
* In attempting to obtain the response, the ARDP library will wait
* for a response and retry an appropriate number of times as defined
* by timeout and retries (both static variables). It will collect
* however many packets form the reply, and return them in the
* request structue.
*
* ARGS: req Request structure holding packets to send
* and to receive the response
* hname Hostname including optional port in parentheses
* dest Pointer to destination address
* ttwait Time to wait in microseconds
*
* MODIFIES: dest If pointer to empty address
* req Fills in ->recv and frees ->trns
*
* NOTE: In preparing packets for transmission, the packets
* are modified. Once the full response has been received,
* the packets that were sent are freed.
*/
int
ardp_send(RREQ req, /* Request structure to use (in, out) */
char *dname, /* Hostname (and port) of destination */
struct sockaddr_in *dest, /* Pointer to destination address */
int ttwait) /* Time to wait in microseconds */
{
char hostnoport[400];/* Hostname without port */
char *openparen; /* Start of port in dname */
int req_udp_port; /* UDP port from hostname */
#ifdef OLD_GETHOSTBYNAME /* unused */
struct hostent *host; /* Host info from gethostbyname */
#endif
PTEXT ptmp; /* Temporary packet pointer */
int DpfStmp; /* Used when disabling prospero */
int tmp; /* To temporarily hold return values */
p_clear_errors();
if((ardp_port < 0) && (tmp = ardp_init())) return(tmp);
if(req->status == ARDP_STATUS_FREE) {
fprintf(stderr,"Attempt to send free RREQ\n");
abort();
return(perrno = ARDP_BAD_REQ);
}
while(req->outpkt) {
req->outpkt->seq = ++(req->trns_tot);
ptmp = req->outpkt;
EXTRACT_ITEM(ptmp,req->outpkt);
APPEND_ITEM(ptmp,req->trns);
}
if(pfs_debug >= 9) {
fprintf(stderr, "In ardp_send - sending to %s\n", dname);
ptmp = req->trns;
while(ptmp) {
fprintf(stderr,"Packet %d:\n",ptmp->seq);
ardp_showbuf(ptmp->text, ptmp->length, stderr);
putc('\n', stderr);
ptmp = ptmp->next;
}
}
/* Assign connection ID */
req->cid = ardp_next_cid();
/* Resolve the host name, address, and port arguments */
/* If we were given the host address, then use it. Otherwise */
/* lookup the hostname. If we were passed a host address of */
/* 0, we must lookup the host name, then replace the old value */
if(!dest || (dest->sin_addr.s_addr == 0)) {
/* I we have a null host name, return an error */
if((dname == NULL) || (*dname == '\0')) {
if (pfs_debug >= 1)
fprintf(stderr, "ardp_send: Null hostname specified\n");
return(perrno = ARDP_BAD_HOSTNAME);
}
/* If a port is included, save it away */
if(openparen = strchr(dname,'(')) {
sscanf(openparen+1,"%d",&req_udp_port);
if(req_udp_port) req->peer_port = htons(req_udp_port);
strncpy(hostnoport,dname,399);
if((openparen - dname) < 400) {
*(hostnoport + (openparen - dname)) = '\0';
dname = hostnoport;
}
}
#ifdef OLD_GETHOSTBYNAME
DISABLE_PFS_START();
assert(P_IS_THIS_THREAD_MASTER());
if((host = gethostbyname(dname)) == NULL) {
DISABLE_PFS_END();
/* Check if a numeric address */
req->peer.sin_family = AF_INET;
req->peer_addr.s_addr = inet_addr(dname);
if(req->peer_addr.s_addr == -1) {
if (pfs_debug >= 1)
fprintf(stderr, "ardp: Can't resolve host %s\n", dname);
return(perrno = ARDP_BAD_HOSTNAME);
}
}
else {
DISABLE_PFS_END();
req->peer.sin_family = host->h_addrtype;
bcopy(host->h_addr, (char *)&(req->peer_addr),
host->h_length);
}
#else
/* New way of doing things */
if (ardp_hostname2addr(dname, &req->peer_addr))
return perrno = ARDP_BAD_HOSTNAME;
#endif
}
else bcopy(dest, &(req->peer), S_AD_SZ);
/* If no port set, use default port */
if(req->peer_port == 0) req->peer_port = ardp_def_port_no;
/* If dest was set, but zero, fill it in */
if(dest && (dest->sin_addr.s_addr == 0))
bcopy(&(req->peer), dest, S_AD_SZ);
if(tmp = ardp_headers(req)) return(tmp);
req->status = ARDP_STATUS_ACTIVE;
APPEND_ITEM(req,ardp_activeQ);
req->wait_till.tv_sec = time(NULL) + req->timeout_adj.tv_sec;
ardp_xmit(req, req->pwindow_sz);
return(ardp_retrieve(req,ttwait));
}
/*
* ardp_init - Open socket and bind port for network operations
*
* ardp_init attempts to determine the default destination port.
* It then opens a socket for network operations and attempts
* to bind it to an available privleged port. It tries to bind to a
* privileged port so that its peer can tell it is communicating with
* a "trusted" program. If it can not bind a priveleged port, then
* it also returns successfully since the system will automatically
* assign a non-priveleged port later, in which case the peer will
* assume that it is communicating with a non-trusted program. It
* is expected that in the normal case, we will fail to bind the
* port since most applications calling this routine should NOT
* be setuid.
*/
static int
ardp_init()
{
struct servent *sp; /* Entry from services file */
struct sockaddr_in us; /* Our address */
int DpfStmp;/* Used when disabling prospero */
int tmp; /* For stepping through ports */
/* Determine default udp port to use */
DISABLE_PFS_START();
assert(P_IS_THIS_THREAD_MASTER()); /*SOLARIS: getservbyname MT-Unsafe */
if ((sp = getservbyname(ARDP_DEFAULT_PEER,"udp")) == 0) {
if (pfs_debug >= 10)
fprintf(stderr, "ardp: udp/%s unknown service - using %d\n",
ARDP_DEFAULT_PEER, ARDP_DEFAULT_PORT);
ardp_def_port_no = htons((u_short) ARDP_DEFAULT_PORT);
}
else ardp_def_port_no = sp->s_port;
DISABLE_PFS_END();
if (pfs_debug >= 10)
fprintf(stderr,"default udp port is %d\n", ntohs(ardp_def_port_no));
/* Open the local socket from which packets will be sent */
errno=0;
if((ardp_port = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
if (pfs_debug >= 1)
fprintf(stderr,"ardp: Can't open socket - %s\n",
unixerrstr());
return(perrno = ARDP_UDP_CANT);
}
#ifndef ARDP_NONPRIVED
/* Try to bind it to a privileged port - loop through candidate */
/* ports trying to bind. If failed, that's OK, we will let the */
/* system assign a non-privileged port later */
bzero((char *)&us, sizeof(us));
us.sin_family = AF_INET;
for(tmp = ARDP_FIRST_PRIVP; tmp < ARDP_FIRST_PRIVP+ARDP_NUM_PRIVP;tmp++) {
us.sin_port = htons((u_short) tmp);
if(bind(ardp_port, (struct sockaddr *)&us, sizeof(us)) == 0)
return(ARDP_SUCCESS);
if(errno != EADDRINUSE) return(ARDP_SUCCESS);
}
#endif /* ARDP_NONPRIVED */
return(ARDP_SUCCESS);
}
/*
* ardp_next_cid - return next connection ID in network byte order
*
* ardp_next_cid returns the next connection ID to be used
* after first converting it to network byte order.
*/
static short ardp_next_cid()
{
static unsigned short next_conn_id = 0; /* Next conn id to use */
static int last_pid = 0; /* Reset after forks */
int pid = getpid();
/* If we did a fork, reinitialize */
if(last_pid != pid) {
if(ardp_port >= 0) close(ardp_port);
ardp_port = -1;
next_conn_id = 0;
ardp_init();
}
/* Find first connection ID */
assert(P_IS_THIS_THREAD_MASTER()); /* rand and srand are unsafe */
if(next_conn_id == 0) {
srand(pid+time(0));
next_conn_id = rand();
last_pid = pid;
}
if(++next_conn_id == 0) ++next_conn_id;
return(htons(next_conn_id));
}