#include <NetInfo/config.h>
#include <NetInfo/syslock.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
#include <rpc/pmap_clnt.h>
#include <sys/time.h>
#include <NetInfo/socket_lock.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <sys/types.h>
#ifdef NeXT
#include <libc.h>
#endif
static syslock *socket_syslock = NULL;
extern int bindresvport(int, struct sockaddr_in *);
static struct timeval timeout = { 4, 0 };
static struct timeval tottimeout = { 20, 0 };
static struct timeval bind_retry = {0, 250 * 1000};
static u_short
sl_pmap_getport(address, program, version, protocol)
struct sockaddr_in *address;
u_long program;
u_long version;
u_int protocol;
{
u_short port = 0;
int sock = -1;
register CLIENT *client;
struct pmap parms;
socket_lock();
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
socket_unlock();
if (sock < 0) return (0);
address->sin_port = htons(PMAPPORT);
client = clntudp_bufcreate(address, PMAPPROG,
PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
if (client != (CLIENT *)NULL)
{
parms.pm_prog = program;
parms.pm_vers = version;
parms.pm_prot = protocol;
parms.pm_port = 0;
if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
xdr_u_short, &port, tottimeout) != RPC_SUCCESS)
{
rpc_createerr.cf_stat = RPC_PMAPFAILURE;
clnt_geterr(client, &rpc_createerr.cf_error);
port = 0;
}
else if (port == 0)
{
rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
}
}
socket_lock();
if (client != NULL) clnt_destroy(client);
close(sock);
socket_unlock();
address->sin_port = 0;
return (port);
}
int
socket_close(int sock)
{
int ret;
socket_lock();
ret = close(sock);
socket_unlock();
return (ret);
}
int
socket_connect(struct sockaddr_in *raddr, int prog, int vers)
{
int sock;
int errno_saved;
bool_t got_port;
int i;
#define MAX_RESV_RETRY 40
if (raddr->sin_port == 0) {
u_short port;
if ((port = sl_pmap_getport(raddr, prog, vers,
IPPROTO_TCP)) == 0) {
syslog(LOG_DEBUG,
"socket_open: %s",
clnt_spcreateerror("pmap_getport failed"));
return (-1);
}
raddr->sin_port = htons(port);
}
socket_lock();
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
errno_saved = errno;
socket_unlock();
if (sock < 0) {
syslog(LOG_WARNING,
"socket_connect: couldn't get socket - %s",
strerror(errno_saved));
return (-1);
}
if (0 != bindresvport(sock, (struct sockaddr_in *)0)) {
if (EADDRINUSE != errno) {
socket_close(sock);
return(-1);
}
syslog(LOG_WARNING,
"socket_connect: retrying reserved port acquisition - %m");
for (i = 0, got_port = FALSE; i < MAX_RESV_RETRY; i++) {
select(0, NULL, NULL, NULL, &bind_retry);
if (0 == bindresvport(sock, (struct sockaddr_in *)0)) {
syslog(LOG_INFO,
"socket_connect: got reserved port after %d tr%s",
i + 1, i != 0 ? "ies" : "y");
got_port = TRUE;
break;
}
}
if (!got_port) {
syslog(LOG_ERR,
"socket_connect: couldn't get reserved port after "
"%d tries; last error was %m", i);
(void)socket_close(sock);
return(-1);
}
}
if (connect(sock, (struct sockaddr *)raddr,
sizeof(*raddr)) < 0) {
syslog(LOG_WARNING, "socket_connect: couldn't connect() - %m");
socket_close(sock);
return (-1);
}
return (sock);
}
int
socket_open(
struct sockaddr_in *raddr,
int prog,
int vers
)
{
int sock;
int errno_saved;
if (raddr->sin_port == 0) {
u_short port;
if ((port = sl_pmap_getport(raddr, prog, vers,
IPPROTO_UDP)) == 0) {
syslog(LOG_DEBUG,
"socket_open: %s",
clnt_spcreateerror("pmap_getport failed"));
return (-1);
}
raddr->sin_port = htons(port);
}
socket_lock();
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
errno_saved = errno;
socket_unlock();
if (sock < 0) {
syslog(LOG_WARNING, "socket_open: couldn't get socket - %s",
strerror(errno_saved));
return (-1);
}
if (0 != bindresvport(sock, (struct sockaddr_in *)0)) {
socket_unlock();
syslog(LOG_WARNING,
"socket_open: retrying reserved port acquisition - %m");
select(0, NULL, NULL, NULL, &bind_retry);
if (0 != bindresvport(sock, (struct sockaddr_in *)0)) {
syslog(LOG_ERR,
"socket_open: couldn't get reserved port - %m");
}
(void)socket_close(sock);
return(-1);
}
return (sock);
}
void
socket_lock(void)
{
if (socket_syslock == NULL) socket_syslock = syslock_new(FALSE);
syslock_lock(socket_syslock);
}
void
socket_unlock(void)
{
syslock_unlock(socket_syslock);
}