/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public * Source License Version 1.0 (the 'License'). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License." * * @APPLE_LICENSE_HEADER_END@ */ #import "Server.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "automount.h" #import "log.h" #import "mount.h" #import "AMString.h" #define MOUNT_PROG_UNKNOWN 0 #define MOUNT_PROG_V1 1 #define MOUNT_PROG_V2 2 #define MOUNT_PROG_V3 3 #define NFS_PROG_UNKNOWN 0 #define NFS_PROG_V1 1 #define NFS_PROG_V2 2 #define NFS_PROG_V3 3 #define LIFE_LATENCY 120 #define DEATH_LATENCY 60 #ifndef __APPLE__ #import #define EBADRPC ECONNREFUSED #define ERPCMISMATCH ECONNREFUSED #define EAUTH ECONNREFUSED #define EPROGUNAVAIL ECONNREFUSED #define EPROCUNAVAIL ECONNREFUSED #endif extern u_short getport(struct sockaddr_in *, u_long, u_long, u_int); static char * mnt_strerror(int s) { if (s == 0) return "No error"; if (s == 1) return "Not owner"; if (s == 2) return "No such file or directory"; if (s == 5) return "I/O error"; if (s == 13) return "Permission denied"; if (s == 20) return "Not a directory"; if (s == 22) return "Invalid argument"; if (s == 63) return "Filename too long"; if (s == 10004) return "Operation not supported"; return "Server failure"; } static unsigned int rpcerr2errno(int r, int e) { switch (r) { case RPC_SUCCESS: return 0; case RPC_CANTENCODEARGS: return EBADRPC; case RPC_CANTDECODERES: return EBADRPC; case RPC_CANTSEND: return e; case RPC_CANTRECV: return e; case RPC_TIMEDOUT: return ETIMEDOUT; case RPC_VERSMISMATCH: return ERPCMISMATCH; case RPC_AUTHERROR: return EAUTH; case RPC_PROGUNAVAIL: return EPROGUNAVAIL; case RPC_PROGVERSMISMATCH: return ERPCMISMATCH; case RPC_PROCUNAVAIL: return EPROCUNAVAIL; case RPC_CANTDECODEARGS: return EBADRPC; case RPC_SYSTEMERROR: return e; case RPC_UNKNOWNHOST: return EHOSTUNREACH; case RPC_UNKNOWNPROTO: return EPROTONOSUPPORT; case RPC_PMAPFAILURE: return e; case RPC_PROGNOTREGISTERED: return EPROGUNAVAIL; case RPC_FAILED: return e; } return EBADRPC; } @implementation Server + (BOOL)isMyAddress:(unsigned int)a mask:(unsigned int)m { struct ifconf ifc; struct ifreq *ifr; char buf[1024]; int offset, addrlen; int sock; unsigned int me; me = htonl(INADDR_LOOPBACK); if ((a & m) == (me & m)) return YES; sock = socket(AF_INET, SOCK_DGRAM, 0); if (sock < 0) return NO; ifc.ifc_len = sizeof(buf); ifc.ifc_buf = buf; if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0) { close(sock); return NO; } addrlen = sizeof(struct ifreq) - IFNAMSIZ; offset = 0; while (offset <= ifc.ifc_len) { ifr = (struct ifreq *)(ifc.ifc_buf + offset); #ifdef __APPLE__ offset += IFNAMSIZ; if (ifr->ifr_addr.sa_len > addrlen) offset += ifr->ifr_addr.sa_len; else offset += addrlen; #else offset += sizeof(struct ifreq); #endif if (ifr->ifr_addr.sa_family != AF_INET) continue; if (ioctl(sock, SIOCGIFFLAGS, ifr) < 0) continue; if ((ifr->ifr_flags & IFF_UP) && (!(ifr->ifr_flags & IFF_LOOPBACK))) { me = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr; if ((a & m) == (me & m)) { close(sock); return YES; } } } close(sock); return NO; } + (BOOL)isMyAddress:(String *)addr { if (addr == nil) return NO; return [Server isMyAddress:inet_addr([addr value]) mask:(unsigned int)-1]; } + (BOOL)isMyNetwork:(String *)net { char *s, *p; unsigned int bits, a, i, x, m; if (net == nil) return NO; s = malloc([net length] + 1); strcpy(s, [net value]); p = strchr(s, '/'); if (p == NULL) { free(s); return [Server isMyAddress:net]; } *p = '\0'; p++; bits = atoi(p); if (bits == 0) { free(s); return [Server isMyAddress:net]; } a = inet_addr(s); free(s); bits = 33 - bits; m = 0; for (i = 1, x = 1; i < bits; i++, x *= 2) m |= x; m = ~m; return [Server isMyAddress:a mask:m]; } - (Server *)initWithName:(String *)servername { char hn[1024]; char *p; int i; [super init]; if (servername == nil) { [self release]; return nil; } pings = 5; timeout = 4; address = 0; gethostname(hn, 1024); isLocalHost = NO; if (!strcmp("localhost", [servername value])) isLocalHost = YES; if ((!isLocalHost) && (!strcmp(hn, [servername value]))) isLocalHost = YES; if (!isLocalHost) { p = strchr(hn, '.'); if (p != NULL) { *p = '\0'; if (!strcmp(hn, [servername value])) isLocalHost = YES; } } if (isLocalHost) address = htonl(INADDR_LOOPBACK); myname = [servername retain]; for (i = 0; i < 4; i++) { port[i] = 0; mountClient[i] = NULL; lastTime[i] = 0; isDead[i][0] = NO; isDead[i][1] = NO; } return self; } - (void)setTimeout:(unsigned int)t { if (t == 0) timeout = 1; else timeout = t; } - (String *)name { return myname; } - (BOOL)isLocalHost { return isLocalHost; } - (unsigned long)address { struct hostent *h; if (address != 0) return address; h = gethostbyname([myname value]); if (h == NULL) return 0; bcopy(h->h_addr, &address, sizeof(unsigned long)); return address; } - (void)reset { int i; CLIENT *cl; for (i = 0; i < 4; i++) { if (mountClient[i] != NULL) { cl = (CLIENT *)mountClient[i]; if (cl->cl_auth) { auth_destroy(cl->cl_auth); } else { sys_msg(debug, LOG_INFO, "[Server reset]: NULL cl_auth in non-NULL connection block for server '%s'?", [[self name] value]); } clnt_destroy(cl); mountClient[i] = NULL; } port[i] = 0; } } - (unsigned int)getNFSPort:(unsigned short *)p version:(unsigned int)v protocol:(unsigned int)proto { struct sockaddr_in sin; struct timeval tv; CLIENT *cl; int i, s, delta, px; unsigned long addr; BOOL mort; if (!((v == 2) || (v == 3))) return ERPCMISMATCH; gettimeofday(&tv, NULL); delta = tv.tv_sec - lastTime[v]; if (delta > LIFE_LATENCY) port[v] = 0; if (port[v] != 0) { *p = port[v]; return 0; } if (mountClient[v] != NULL) { cl = (CLIENT *)mountClient[v]; auth_destroy(cl->cl_auth); clnt_destroy(cl); } mountClient[v] = NULL; px = 0; if (proto == IPPROTO_TCP) px = 1; mort = isDead[v][px]; if (mort && (delta < DEATH_LATENCY)) { sys_msg(debug, LOG_INFO, "Assuming %s NFS_V%d/%s is still unavailable (%d seconds remain)", [myname value], v, (px == 0) ? "UDP" : "TCP", DEATH_LATENCY - delta); return EHOSTDOWN; } isDead[v][px] = NO; addr = [self address]; if (addr == 0) { sys_msg(debug, LOG_ERR, "Can't find address for %s", [myname value]); isDead[v][px] = YES; return 0; } bzero((char *)&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = addr; sin.sin_port = 0; port[v] = getport(&sin, NFS_PROGRAM, v, proto); lastTime[v] = tv.tv_sec; if (port[v] == 0) { sys_msg(debug, LOG_NOTICE, "Can't get NFS_V%d/%s port for %s", v, (proto == IPPROTO_TCP) ? "TCP" : "UDP", [myname value]); isDead[v][px] = YES; if (rpc_createerr.cf_stat == RPC_PMAPFAILURE) { for (i = 0; i < 4; i++) { isDead[i][0] = YES; isDead[i][1] = YES; } return EHOSTDOWN; } return rpcerr2errno(rpc_createerr.cf_stat, ECONNREFUSED); } bzero((char *)&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = addr; tv.tv_sec = timeout / pings; if (tv.tv_sec == 0) tv.tv_sec = 1; tv.tv_usec = 0; s = RPC_ANYSOCK; cl = NULL; /* * Use MOUNTPROG V1 for NFS V2. * Use MOUNTPROG V3 for NFS V3. */ i = 1; if (v == 3) i = 3; if (proto == IPPROTO_TCP) { sys_msg(debug, LOG_DEBUG, "Creating TCP MOUNTPROG client for server %s: %s", inet_ntoa(sin.sin_addr), clnt_spcreateerror("clnttcp_create")); cl = clnttcp_create(&sin, MOUNTPROG, i, &s, 0, 0); } else { sys_msg(debug, LOG_DEBUG, "Creating UDP MOUNTPROG client for server %s: %s", inet_ntoa(sin.sin_addr), clnt_spcreateerror("clntudp_create")); cl = clntudp_create(&sin, MOUNTPROG, i, tv, &s); } if (cl == NULL) { sys_msg(debug, LOG_ERR, "Can't create MOUNTPROG client for server %s: %s", inet_ntoa(sin.sin_addr), clnt_spcreateerror("clntudp_create")); return rpc_createerr.cf_error.re_errno; } cl->cl_auth = authunix_create_default(); mountClient[v] = (void *)cl; *p = port[v]; return 0; } - (unsigned int)getHandle:(void *)fh size:(int *)s port:(unsigned short *)p forFile:(String *)filename version:(unsigned int)v protocol:(unsigned int)proto { int status; struct timeval tv; char *dir; fhstatus mount_res_2; mountres3 mount_res_3; unsigned int mount_status; CLIENT *cl; struct rpc_err rpcerr; if (!((v == 2) || (v == 3))) return ERPCMISMATCH; status = [self getNFSPort:p version:v protocol:proto]; if (status != 0) return status; tv.tv_sec = timeout; if (tv.tv_sec == 0) tv.tv_sec = 1; tv.tv_usec = 0; status = RPC_FAILED; cl = (CLIENT *)mountClient[v]; if (cl == NULL) return status; mount_status = (unsigned int)-1; dir = [filename value]; memset(&mount_res_2, 0, sizeof(fhstatus)); memset(&mount_res_3, 0, sizeof(mountres3)); if (v == 2) { status = clnt_call(cl, MOUNTPROC_MNT, (xdrproc_t)xdr_dirpath, &dir, (xdrproc_t)xdr_fhstatus, &mount_res_2, tv); if (status == RPC_SUCCESS) mount_status = mount_res_2.fhs_status; } else { status = clnt_call(cl, MOUNTPROC_MNT, (xdrproc_t)xdr_dirpath, &dir, (xdrproc_t)xdr_mountres3, &mount_res_3, tv); if (status == RPC_SUCCESS) mount_status = mount_res_3.fhs_status; } if (status != RPC_SUCCESS) { sys_msg(debug, LOG_ERR, "RPC mount version %d %s:%s status: %s", v, [myname value], [filename value], clnt_sperrno(status)); clnt_geterr(cl, &rpcerr); return rpcerr2errno(rpcerr.re_status, ECONNABORTED); } if (mount_status != 0) { sys_msg(debug, LOG_ERR, "mount (NFSV%d) %s:%s - %s", v, [myname value], [filename value], mnt_strerror(mount_status)); if (v == 2) { (void)clnt_freeres(cl, (xdrproc_t)xdr_fhstatus, &mount_res_2); } else { (void)clnt_freeres(cl, (xdrproc_t)xdr_mountres3, &mount_res_3); } return mount_status; } if (v == 2) { *s = FHSIZE; memmove(fh, mount_res_2.fhstatus_u.fhs_fhandle, *s); (void)clnt_freeres(cl, (xdrproc_t)xdr_fhstatus, &mount_res_2); } else { *s = mount_res_3.mountres3_u.mountinfo.fhandle.fhandle3_len; memmove(fh, mount_res_3.mountres3_u.mountinfo.fhandle.fhandle3_val, *s); (void)clnt_freeres(cl, (xdrproc_t)xdr_mountres3, &mount_res_3); } return 0; } - (void)dealloc { CLIENT *cl; int i; for (i = 0; i < 4; i++) { if (mountClient[i] != NULL) { cl = (CLIENT *)mountClient[i]; auth_destroy(cl->cl_auth); clnt_destroy(cl); } } [myname release]; [super dealloc]; } @end