/* * 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 * . */ #include #include #include #include #include #include #include #include #include #include #define OLD_GETHOSTBYNAME /* do it the old way, since we're on the client */ #ifdef PROSPERO #include #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)); }