summaryrefslogtreecommitdiffstats
path: root/lib/libc/yp
diff options
context:
space:
mode:
authorwpaul <wpaul@FreeBSD.org>1996-06-01 05:08:31 +0000
committerwpaul <wpaul@FreeBSD.org>1996-06-01 05:08:31 +0000
commit469d167428c75a0d69586d174ee8c63dffb53679 (patch)
tree4505541c7cc1de082ab04bbd514233878626d5cf /lib/libc/yp
parentf96a6bec7fc3dcdc42d9d842779178e93798aac2 (diff)
downloadFreeBSD-src-469d167428c75a0d69586d174ee8c63dffb53679.zip
FreeBSD-src-469d167428c75a0d69586d174ee8c63dffb53679.tar.gz
Make _yp_dobind() a litle smarter:
Now that we preserve RPC handles instead of rebuilding them each time a ypcln function is called, we have to be careful about keeping our sockets in a sane state. It's possible that the caller may call a ypclnt function, and then decide to close all its file descriptors. This would also close the socket descriptor held by the yplib code. Worse, it could re-open the same descriptor number for its own use. If it then calls another ypclnt function, the subsequent RPC will fail because the socket will either be gone or replaced with Something Completely Different. The yplib code will recover by rebinding, but it doing so it may wreck the descriptor which now belongs to the caller. To fix this, _yp_dobind() needs to label the descriptor somehow so that it can test it later to make sure it hasn't been altered between ypclnt calls. It does this by binding the socket, thus associating a port number with it. It then saves this port number in the dom_local_port member of the dom_binding structure for the given domain. When _yp_dobind() is called again (which it is at the start of each ypclnt function), it checks to see if the domain is already bound, and if it is, it does a getsockname() on the socket and compares the port number to the one it saved. If the getsockname() fails, or the port number doesn't match, it abandons the socket and sets up a new client handle. This still incurs some syscall overhead, which is what I was trying to avoid, but it's still not as bad as before.
Diffstat (limited to 'lib/libc/yp')
-rw-r--r--lib/libc/yp/yplib.c45
1 files changed, 43 insertions, 2 deletions
diff --git a/lib/libc/yp/yplib.c b/lib/libc/yp/yplib.c
index da4d3b7..99e7693 100644
--- a/lib/libc/yp/yplib.c
+++ b/lib/libc/yp/yplib.c
@@ -58,7 +58,7 @@ struct dom_binding {
u_short dom_server_port;
int dom_socket;
CLIENT *dom_client;
- u_short dom_local_port;
+ u_short dom_local_port; /* now I finally know what this is for. */
long dom_vers;
};
@@ -221,6 +221,8 @@ _yp_dobind(dom, ypdb)
CLIENT *client;
int new = 0, r;
int retries = 0;
+ struct sockaddr_in check;
+ int checklen = sizeof(struct sockaddr_in);
gpid = getpid();
if( !(pid==-1 || pid==gpid) ) {
@@ -246,12 +248,29 @@ _yp_dobind(dom, ypdb)
if( strcmp(dom, ysd->dom_domain) == 0)
break;
+
if(ysd==NULL) {
ysd = (struct dom_binding *)malloc(sizeof *ysd);
bzero((char *)ysd, sizeof *ysd);
ysd->dom_socket = -1;
ysd->dom_vers = 0;
new = 1;
+ } else {
+ /* Check the socket -- may have been hosed by the caller. */
+ if (getsockname(ysd->dom_socket, (struct sockaddr *)&check,
+ &checklen) == -1 || check.sin_family != AF_INET ||
+ check.sin_port != ysd->dom_local_port) {
+ /* Socket became bogus somehow... need to rebind. */
+ int save, sock;
+
+ sock = ysd->dom_socket;
+ save = dup(ysd->dom_socket);
+ clnt_destroy(ysd->dom_client);
+ ysd->dom_vers = 0;
+ ysd->dom_client = NULL;
+ sock = dup2(save, sock);
+ close(save);
+ }
}
again:
@@ -270,6 +289,7 @@ again:
if(ysd->dom_client) {
clnt_destroy(ysd->dom_client);
ysd->dom_client = NULL;
+ ysd->dom_socket = -1;
}
sprintf(path, "%s/%s.%d", BINDINGDIR, dom, 2);
if( (fd=open(path, O_RDONLY)) == -1) {
@@ -323,6 +343,7 @@ skipit:
if(ysd->dom_client) {
clnt_destroy(ysd->dom_client);
ysd->dom_client = NULL;
+ ysd->dom_socket = -1;
}
bzero((char *)&clnt_sin, sizeof clnt_sin);
clnt_sin.sin_family = AF_INET;
@@ -354,8 +375,11 @@ skipit:
if(r != RPC_SUCCESS) {
clnt_destroy(client);
ysd->dom_vers = -1;
- if (r == RPC_PROGUNAVAIL || r == RPC_PROCUNAVAIL)
+ if (r == RPC_PROGUNAVAIL || r == RPC_PROCUNAVAIL) {
+ if (new)
+ free(ysd);
return(YPERR_YPBIND);
+ }
fprintf(stderr,
"YP: server for domain %s not responding, retrying\n", dom);
goto again;
@@ -396,6 +420,23 @@ gotit:
}
if( fcntl(ysd->dom_socket, F_SETFD, 1) == -1)
perror("fcntl: F_SETFD");
+ /*
+ * We want a port number associated with this socket
+ * so that we can check its authenticity later.
+ */
+ checklen = sizeof(struct sockaddr_in);
+ bzero((char *)&check, checklen);
+ bind(ysd->dom_socket, (struct sockaddr *)&check, checklen);
+ check.sin_family = AF_INET;
+ if (!getsockname(ysd->dom_socket,
+ (struct sockaddr *)&check, &checklen)) {
+ ysd->dom_local_port = check.sin_port;
+ } else {
+ clnt_destroy(ysd->dom_client);
+ if (new)
+ free(ysd);
+ return(YPERR_YPBIND);
+ }
}
if(new) {
OpenPOWER on IntegriCloud