/*
* 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 <sys/types.h>
#import <netinet/in.h>
#import <arpa/inet.h>
#import <netdb.h>
#import <unistd.h>
#import <stdlib.h>
#import <string.h>
#import <sys/socket.h>
#import <net/if.h>
#import <sys/ioctl.h>
#import <rpc/types.h>
#import <rpc/xdr.h>
#import <rpc/auth.h>
#import <rpc/clnt.h>
#import <rpc/svc.h>
#import <errno.h>
#import "nfs_prot.h"
#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 <libc.h>
#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];
[super init];
if (servername == nil)
{
[self release];
return nil;
}
pings = 5;
timeout = 4;
address = 0;
gethostname(hn, 1024);
isLocalHost = NO;
if ((!strcmp(hn, [servername value]))
|| (!strcmp("localhost", [servername value])))
{
isLocalHost = YES;
myname = [servername retain];
address = htonl(INADDR_LOOPBACK);
[self reset];
return self;
}
myname = [servername retain];
[self reset];
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;
for (i = 0; i < 4; i++)
{
port[i] = 0;
lastTime[i] = 0;
isDead[i][0] = NO;
isDead[i][1] = NO;
}
}
- (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 [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 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 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)
cl = clnttcp_create(&sin, MOUNTPROG, i, &s, 0, 0);
else
cl = clntudp_create(&sin, MOUNTPROG, i, tv, &s);
if (cl == NULL)
{
sys_msg(debug, LOG_ERR, "Can't create MOUNTPROG client for server 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, xdr_dirpath, &dir,
xdr_fhstatus, &mount_res_2, tv);
if (status == RPC_SUCCESS) mount_status = mount_res_2.fhs_status;
}
else
{
status = clnt_call(cl, MOUNTPROC_MNT, xdr_dirpath, &dir,
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 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 v, [myname value], [filename value], mnt_strerror(mount_status));
return mount_status;
}
if (v == 2)
{
*s = FHSIZE;
memmove(fh, mount_res_2.fhstatus_u.fhs_fhandle, *s);
}
else
{
*s = mount_res_3.mountres3_u.mountinfo.fhandle.fhandle3_len;
memmove(fh, mount_res_3.mountres3_u.mountinfo.fhandle.fhandle3_val, *s);
}
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);
}
}
[super dealloc];
}
@end