mirror of
https://github.com/mabam/afpfs-ng-mac.git
synced 2025-01-06 01:32:09 +00:00
807 lines
18 KiB
C
807 lines
18 KiB
C
|
|
/*
|
|
* afp.c
|
|
*
|
|
* Copyright (C) 2006 Alex deVries
|
|
* Portions copyright (C) 2007 Derrik Pates
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
#include "afpfs-ng/afp.h"
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
#include "afpfs-ng/afp_protocol.h"
|
|
#include "afpfs-ng/libafpclient.h"
|
|
#include "server.h"
|
|
#include "afpfs-ng/dsi.h"
|
|
#include "dsi_protocol.h"
|
|
#include "afpfs-ng/utils.h"
|
|
#include "afp_replies.h"
|
|
#include "afp_internal.h"
|
|
#include "did.h"
|
|
#include "forklist.h"
|
|
#include "afpfs-ng/codepage.h"
|
|
|
|
struct afp_versions afp_versions[] = {
|
|
{ "AFPVersion 1.1", 11 },
|
|
{ "AFPVersion 2.0", 20 },
|
|
{ "AFPVersion 2.1", 21 },
|
|
{ "AFP2.2", 22 },
|
|
{ "AFPX03", 30 },
|
|
{ "AFP3.1", 31 },
|
|
{ "AFP3.2", 32 },
|
|
{ NULL, 0}
|
|
};
|
|
|
|
static int afp_blank_reply(struct afp_server *server, char * buf, unsigned int size, void * ignored);
|
|
|
|
int (*afp_replies[])(struct afp_server * server,char * buf, unsigned int len, void * other) = {
|
|
NULL, afp_byterangelock_reply, afp_blank_reply, NULL,
|
|
afp_blank_reply, NULL, afp_createdir_reply, afp_blank_reply, /* 0 - 7 */
|
|
afp_blank_reply, afp_enumerate_reply, afp_blank_reply, afp_blank_reply,
|
|
NULL, NULL, NULL, NULL, /* 8 - 15 */
|
|
afp_getsrvrparms_reply, afp_getvolparms_reply, afp_login_reply, afp_login_reply,
|
|
afp_blank_reply, afp_mapid_reply, afp_mapname_reply, afp_blank_reply, /*16 - 23 */
|
|
afp_volopen_reply, NULL, afp_openfork_reply, afp_read_reply,
|
|
afp_blank_reply, afp_blank_reply, afp_blank_reply, afp_blank_reply, /*24 - 31 */
|
|
NULL, afp_write_reply, afp_getfiledirparms_reply, afp_blank_reply,
|
|
afp_changepassword_reply, afp_getuserinfo_reply, afp_getsrvrmsg_reply, NULL, /*32 - 39 */
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, /*40 - 47 */
|
|
afp_opendt_reply, afp_blank_reply, NULL, afp_geticon_reply,
|
|
NULL, NULL, NULL, NULL, /*48 - 55 */
|
|
afp_blank_reply, NULL, afp_getcomment_reply, afp_byterangelockext_reply,
|
|
afp_readext_reply, afp_writeext_reply,
|
|
NULL, NULL, /*56 - 63 */
|
|
afp_getsessiontoken_reply,afp_blank_reply, NULL, NULL,
|
|
afp_enumerateext2_reply, NULL, NULL, NULL, /*64 - 71 */
|
|
afp_listextattrs_reply, NULL, NULL, NULL,
|
|
afp_blank_reply, NULL, afp_blank_reply, afp_blank_reply, /*72 - 79 */
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL,
|
|
|
|
};
|
|
|
|
/*
|
|
* afp_server_identify()
|
|
*
|
|
* Identifies a server
|
|
*
|
|
* Right now, this only does identification using the machine_type
|
|
* given in getsrvrinfo, but this could later use mDNS to get
|
|
* more details.
|
|
*/
|
|
void afp_server_identify(struct afp_server * s)
|
|
{
|
|
if (strcmp(s->machine_type,"Netatalk")==0)
|
|
s->server_type=AFPFS_SERVER_TYPE_NETATALK;
|
|
else if (strcmp(s->machine_type,"AirPort")==0)
|
|
s->server_type=AFPFS_SERVER_TYPE_AIRPORT;
|
|
else if (strcmp(s->machine_type,"Macintosh")==0)
|
|
s->server_type=AFPFS_SERVER_TYPE_MACINTOSH;
|
|
else
|
|
s->server_type=AFPFS_SERVER_TYPE_UNKNOWN;
|
|
}
|
|
|
|
/* This is the simplest afp reply */
|
|
static int afp_blank_reply(struct afp_server *server, char * buf, unsigned int size, void * ignored)
|
|
{
|
|
struct {
|
|
struct dsi_header header __attribute__((__packed__));
|
|
} * reply = (void *) buf;
|
|
return reply->header.return_code.error_code;
|
|
}
|
|
|
|
/* Handle a reply packet */
|
|
int afp_reply(unsigned short subcommand, struct afp_server * server, void * other)
|
|
{
|
|
int ret=0;
|
|
|
|
/* No AFP packet is valid if it is smaller than a DSI header. */
|
|
|
|
if (server->data_read<sizeof(struct dsi_header))
|
|
return -1;
|
|
|
|
if (afp_replies[subcommand]) {
|
|
ret=(*afp_replies[subcommand])(server,
|
|
server->incoming_buffer,
|
|
server->data_read, other);
|
|
} else {
|
|
log_for_client(NULL,AFPFSD,LOG_WARNING,
|
|
"AFP subcommand %d not supported\n",subcommand);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
static struct afp_server * server_base=NULL;
|
|
|
|
int server_still_valid(struct afp_server * server)
|
|
{
|
|
struct afp_server * s = server_base;
|
|
|
|
for (;s;s=s->next)
|
|
if (s==server) return 1;
|
|
return 0;
|
|
}
|
|
|
|
static void add_server(struct afp_server *newserver)
|
|
{
|
|
newserver->next=server_base;
|
|
server_base=newserver;
|
|
}
|
|
|
|
struct afp_server * get_server_base(void)
|
|
{
|
|
return server_base;
|
|
}
|
|
|
|
struct afp_server * find_server_by_signature(char * signature)
|
|
{
|
|
struct afp_server * s;
|
|
|
|
for (s=get_server_base();s;s=s->next) {
|
|
if (memcmp(s->signature,signature,AFP_SIGNATURE_LEN)==0) {
|
|
return s;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
struct afp_server * find_server_by_name(char * name)
|
|
{
|
|
struct afp_server * s;
|
|
for (s=get_server_base(); s; s=s->next) {
|
|
if (strcmp(s->server_name_utf8,name)==0) return s;
|
|
if (strcmp(s->server_name,name)==0) return s;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct afp_server * find_server_by_address(struct addrinfo *address)
|
|
{
|
|
struct afp_server *s;
|
|
|
|
for (s=server_base;s;s=s->next) {
|
|
if (s->used_address != NULL && s->used_address->ai_addr != NULL &&
|
|
address != NULL && address->ai_addr != NULL &&
|
|
bcmp(&s->used_address->ai_addr, &address->ai_addr,
|
|
sizeof(struct sockaddr))==0) {
|
|
return s;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int something_is_mounted(struct afp_server * server)
|
|
{
|
|
int i;
|
|
|
|
|
|
for (i=0;i<server->num_volumes;i++) {
|
|
if (server->volumes[i].mounted != AFP_VOLUME_UNMOUNTED )
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int afp_unmount_all_volumes(struct afp_server * server)
|
|
{
|
|
|
|
int i;
|
|
for (i=0;i<server->num_volumes;i++) {
|
|
if (server->volumes[i].mounted == AFP_VOLUME_MOUNTED) {
|
|
if (afp_unmount_volume(&server->volumes[i]))
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int afp_unmount_volume(struct afp_volume * volume)
|
|
{
|
|
|
|
struct afp_server * server;
|
|
|
|
if (volume==NULL)
|
|
return -1;
|
|
|
|
server=volume->server;
|
|
if (volume->mounted != AFP_VOLUME_MOUNTED) {
|
|
return -1;
|
|
}
|
|
|
|
volume->mounted=AFP_VOLUME_UNMOUNTING;
|
|
|
|
/* close the volume */
|
|
|
|
afp_flush(volume);
|
|
|
|
free_entire_did_cache(volume);
|
|
remove_fork_list(volume);
|
|
if (volume->dtrefnum) afp_closedt(server,volume->dtrefnum);
|
|
volume->dtrefnum=0;
|
|
|
|
if (libafpclient->unmount_volume)
|
|
libafpclient->unmount_volume(volume);
|
|
|
|
volume->mounted=AFP_VOLUME_UNMOUNTED;
|
|
|
|
/* Figure out if this is the last volume of the server */
|
|
|
|
if (something_is_mounted(server)) return 0;
|
|
|
|
/* Logout */
|
|
afp_logout(server,DSI_DONT_WAIT /* don't wait */);
|
|
|
|
afp_server_remove(server);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
void afp_free_server(struct afp_server ** sp)
|
|
{
|
|
struct dsi_request * p, *next;
|
|
struct afp_volume * volumes;
|
|
struct afp_server * server;
|
|
|
|
if (sp==NULL) return;
|
|
|
|
server=*sp;
|
|
|
|
if (!server) return;
|
|
|
|
for (p=server->command_requests;p;) {
|
|
log_for_client(NULL,AFPFSD,LOG_NOTICE,"FSLeft in queue: %p, id: %d command: %d\n", p,p->requestid,p->subcommand);
|
|
next=p->next;
|
|
free(p);
|
|
p=next;
|
|
}
|
|
|
|
volumes=server->volumes;
|
|
|
|
loop_disconnect(server);
|
|
|
|
if (server->incoming_buffer) free(server->incoming_buffer);
|
|
if (server->attention_buffer) free(server->attention_buffer);
|
|
if (volumes) free(volumes);
|
|
|
|
free(server);
|
|
|
|
*sp=NULL;
|
|
}
|
|
|
|
int afp_server_remove(struct afp_server *s)
|
|
{
|
|
|
|
struct dsi_request * p;
|
|
struct afp_server *s2;
|
|
|
|
|
|
if (s==NULL)
|
|
goto out;
|
|
|
|
for (p=s->command_requests;p;p=p->next) {
|
|
pthread_mutex_lock(&p->waiting_mutex);
|
|
p->done_waiting=1;
|
|
pthread_cond_signal(&p->waiting_cond);
|
|
pthread_mutex_unlock(&p->waiting_mutex);
|
|
}
|
|
|
|
if (s==server_base) {
|
|
server_base=s->next;
|
|
afp_free_server(&s);
|
|
goto out;
|
|
}
|
|
|
|
for (s2=server_base;s2;s2=s2->next) {
|
|
if (s==s2->next) {
|
|
s2->next=s->next;
|
|
afp_free_server(&s);
|
|
goto out;
|
|
}
|
|
}
|
|
return -1;
|
|
out:
|
|
return 0;
|
|
|
|
}
|
|
|
|
struct afp_server * afp_server_init(struct addrinfo * address)
|
|
{
|
|
struct afp_server * s;
|
|
struct passwd *pw;
|
|
|
|
if ((s = malloc(sizeof(*s)))==NULL)
|
|
return NULL;
|
|
memset((void *) s, 0, sizeof(*s));
|
|
s->exit_flag = 0;
|
|
s->path_encoding=kFPUTF8Name; /* This is a default */
|
|
s->next=NULL;
|
|
s->bufsize=4096;
|
|
s->incoming_buffer=malloc(s->bufsize);
|
|
|
|
s->attention_quantum=AFP_DEFAULT_ATTENTION_QUANTUM;
|
|
s->attention_buffer=malloc(s->attention_quantum);
|
|
s->attention_len=0;
|
|
|
|
s->connect_state=SERVER_STATE_DISCONNECTED;
|
|
s->address = address;
|
|
|
|
/* FIXME this shouldn't be set here */
|
|
pw=getpwuid(geteuid());
|
|
memcpy(&s->passwd,pw,sizeof(struct passwd));
|
|
return s;
|
|
}
|
|
|
|
static void setup_default_outgoing_token(struct afp_token * token)
|
|
{
|
|
char foo[] = {0x54,0xc0,0x75,0xb0,0x15,0xe6,0x1c,0x13,
|
|
0x86,0x75,0xd2,0xc2,0xfd,0x03,0x4e,0x3b};
|
|
token->length=16;
|
|
memcpy(token->data,foo,16);
|
|
}
|
|
|
|
static int resume_token(struct afp_server * server)
|
|
{
|
|
|
|
struct afp_token outgoing_token;
|
|
time_t now;
|
|
int ret;
|
|
|
|
/* Get the time */
|
|
|
|
time(&now);
|
|
|
|
/* Setup the outgoing token */
|
|
setup_default_outgoing_token(&outgoing_token);
|
|
|
|
ret=afp_getsessiontoken(server,kReconnWithTimeAndID,
|
|
(unsigned int) now,
|
|
&outgoing_token,&server->token);
|
|
|
|
return ret;
|
|
|
|
}
|
|
static int setup_token(struct afp_server * server)
|
|
{
|
|
|
|
time_t now;
|
|
int ret;
|
|
struct afp_token outgoing_token;
|
|
|
|
/* Get the time */
|
|
|
|
time(&now);
|
|
|
|
/* Setup the outgoing token */
|
|
setup_default_outgoing_token(&outgoing_token);
|
|
|
|
ret=afp_getsessiontoken(server,kLoginWithTimeAndID,
|
|
(unsigned int) now,
|
|
&outgoing_token,&server->token);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
int afp_server_login(struct afp_server *server,
|
|
char * mesg, unsigned int *l, unsigned int max)
|
|
{
|
|
int rc;
|
|
|
|
rc=afp_dologin(server,server->using_uam,
|
|
server->username,server->password);
|
|
|
|
switch(rc) {
|
|
case -1:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Could not find a valid UAM\n");
|
|
goto error;
|
|
case kFPAuthContinue:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Authentication method unsupported by AFPFS\n");
|
|
goto error;
|
|
case kFPBadUAM:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Specified UAM is unknown\n");
|
|
goto error;
|
|
case kFPBadVersNum:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Server does not support this AFP version\n");
|
|
case kFPCallNotSupported:
|
|
case kFPMiscErr:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Already logged in\n");
|
|
break;
|
|
case kFPNoServer:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Authentication server not responding\n");
|
|
goto error;
|
|
case kFPPwdExpiredErr:
|
|
case kFPPwdNeedsChangeErr:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Warning: password needs changing\n");
|
|
goto error;
|
|
case kFPServerGoingDown:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Server going down, so I can't log you in.\n");
|
|
goto error;
|
|
case kFPUserNotAuth:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Authentication failed\n");
|
|
goto error;
|
|
case 0: break;
|
|
default:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Unknown error, maybe username/passwd needed?\n");
|
|
goto error;
|
|
}
|
|
|
|
if (server->flags & kSupportsReconnect) {
|
|
/* Get the session */
|
|
|
|
if (server->need_resume) {
|
|
resume_token(server);
|
|
server->need_resume=0;
|
|
} else {
|
|
setup_token(server);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
return 1;
|
|
}
|
|
|
|
|
|
struct afp_volume * find_volume_by_name(struct afp_server * server,
|
|
const char * volname)
|
|
{
|
|
int i;
|
|
struct afp_volume * using_volume=NULL;
|
|
char converted_volname[AFP_VOLUME_NAME_LEN];
|
|
|
|
memset(converted_volname,0,AFP_VOLUME_NAME_LEN);
|
|
|
|
convert_utf8pre_to_utf8dec(volname,strlen(volname),
|
|
converted_volname,AFP_VOLUME_NAME_LEN);
|
|
|
|
for (i=0;i<server->num_volumes;i++) {
|
|
if (strcmp(converted_volname,
|
|
server->volumes[i].volume_name_printable)==0)
|
|
{
|
|
using_volume=&server->volumes[i];
|
|
goto out;
|
|
}
|
|
}
|
|
out:
|
|
|
|
return using_volume;
|
|
}
|
|
|
|
int afp_connect_volume(struct afp_volume * volume, struct afp_server * server,
|
|
char * mesg, unsigned int * l, unsigned int max)
|
|
{
|
|
unsigned short bitmap=
|
|
kFPVolAttributeBit|kFPVolSignatureBit|
|
|
kFPVolCreateDateBit|kFPVolIDBit |
|
|
kFPVolNameBit;
|
|
char new_encoding;
|
|
int ret;
|
|
|
|
if (server->using_version->av_number>=30)
|
|
bitmap|= kFPVolNameBit|kFPVolBlockSizeBit;
|
|
|
|
ret = afp_volopen(volume,bitmap,
|
|
(strlen(volume->volpassword)>0) ? volume->volpassword : NULL);
|
|
switch(ret){
|
|
case kFPAccessDenied:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Incorrect volume password\n");
|
|
goto error;
|
|
case kFPNoErr:
|
|
break;
|
|
case kFPBitmapErr:
|
|
case kFPMiscErr:
|
|
case kFPObjectNotFound:
|
|
case kFPParamErr:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Could not open volume\n");
|
|
goto error;
|
|
case ETIMEDOUT:
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Timed out waiting to open volume\n");
|
|
goto error;
|
|
}
|
|
|
|
/* It is said that if a volume's encoding will be the same
|
|
* the server's. */
|
|
if (volume->attributes & kSupportsUTF8Names)
|
|
new_encoding=kFPUTF8Name;
|
|
else
|
|
new_encoding=kFPLongName;
|
|
|
|
if (new_encoding != server->path_encoding) {
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Volume %s changes the server's encoding\n",
|
|
volume->volume_name_printable);
|
|
}
|
|
|
|
server->path_encoding=new_encoding;
|
|
|
|
if (volume->signature != AFP_VOL_FIXED) {
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Volume %s does not support fixed directories\n",
|
|
volume->volume_name_printable);
|
|
afp_unmount_volume(volume);
|
|
goto error;
|
|
|
|
}
|
|
|
|
if (server->using_version->av_number >=30) {
|
|
if ((volume->server->server_type==AFPFS_SERVER_TYPE_NETATALK) &&
|
|
(~ volume->attributes & kSupportsUnixPrivs)) {
|
|
volume->extra_flags &=~VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX;
|
|
} else {
|
|
volume->extra_flags |= VOLUME_EXTRA_FLAGS_VOL_SUPPORTS_UNIX;
|
|
}
|
|
} else {
|
|
/* This is very odd, but AFP 2.x doesn't give timestamps for directories */
|
|
|
|
}
|
|
|
|
volume->mounted=AFP_VOLUME_MOUNTED;
|
|
|
|
return 0;
|
|
error:
|
|
return 1;
|
|
}
|
|
|
|
int afp_server_reconnect(struct afp_server * s, char * mesg,
|
|
unsigned int *l, unsigned int max)
|
|
{
|
|
int i;
|
|
struct afp_volume * v;
|
|
|
|
if (afp_server_connect(s,0)) {
|
|
*l+=snprintf(mesg,max-*l,"Error resuming connection to %s\n",
|
|
s->server_name_printable);
|
|
return 1;
|
|
}
|
|
|
|
dsi_opensession(s);
|
|
|
|
if(afp_server_login(s,mesg,l,max)) return 1;
|
|
|
|
for (i=0;i<s->num_volumes;i++) {
|
|
v=&s->volumes[i];
|
|
if (strlen(v->mountpoint)) {
|
|
if (afp_connect_volume(v,v->server,mesg,l,max))
|
|
*l+=snprintf(mesg,max-*l,
|
|
"Could not mount %s\n",
|
|
v->volume_name_printable);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int afp_server_connect(struct afp_server *server, int full)
|
|
{
|
|
int error = 0;
|
|
struct timeval t1, t2;
|
|
struct addrinfo *address;
|
|
char log_msg[64];
|
|
char ip_addr[INET6_ADDRSTRLEN];
|
|
|
|
address = server->address;
|
|
while (address)
|
|
{
|
|
switch(address->ai_family)
|
|
{
|
|
case AF_INET6:
|
|
inet_ntop(AF_INET6, &(((struct sockaddr_in6 *)address->ai_addr)->sin6_addr),
|
|
ip_addr, INET6_ADDRSTRLEN);
|
|
break;
|
|
case AF_INET:
|
|
inet_ntop(AF_INET, &(((struct sockaddr_in *)address->ai_addr)->sin_addr),
|
|
ip_addr, INET6_ADDRSTRLEN);
|
|
break;
|
|
default:
|
|
snprintf(ip_addr, 22, "unknown address family");
|
|
break;
|
|
}
|
|
|
|
snprintf(log_msg, sizeof(log_msg), "trying %s ...", ip_addr);
|
|
|
|
log_for_client(NULL, AFPFSD, LOG_NOTICE, log_msg);
|
|
|
|
server->fd = socket(address->ai_family,
|
|
address->ai_socktype, address->ai_protocol);
|
|
|
|
if (server->fd >= 0)
|
|
{
|
|
if (connect(server->fd, address->ai_addr, address->ai_addrlen) == 0)
|
|
break;
|
|
close(server->fd);
|
|
server->fd = -1;
|
|
}
|
|
address = address->ai_next;
|
|
}
|
|
|
|
if(server->fd < 0)
|
|
{
|
|
error = errno;
|
|
goto error;
|
|
}
|
|
|
|
server->exit_flag = 0;
|
|
server->lastrequestid = 0;
|
|
server->connect_state = SERVER_STATE_CONNECTED;
|
|
server->used_address = address;
|
|
|
|
add_server(server);
|
|
|
|
add_fd_and_signal(server->fd);
|
|
if (!full) {
|
|
return 0;
|
|
}
|
|
|
|
/* Get the status, and calculate the transmit time. We use this to
|
|
* calculate our rx quantum. */
|
|
gettimeofday(&t1,NULL);
|
|
|
|
if ((error=dsi_getstatus(server))!=0)
|
|
goto error;
|
|
gettimeofday(&t2,NULL);
|
|
|
|
afp_server_identify(server);
|
|
|
|
if ((t2.tv_sec - t1.tv_sec) > 0)
|
|
server->tx_delay= (t2.tv_sec - t1.tv_sec) * 1000;
|
|
else
|
|
server->tx_delay= (t2.tv_usec - t1.tv_usec) / 1000;
|
|
|
|
/* Calculate the quantum based on our tx_delay and a threshold */
|
|
/* For now, we'll just set a default */
|
|
/* This is the default in 10.4.x where x > 7 */
|
|
server->rx_quantum = 128 * 1024;
|
|
|
|
|
|
return 0;
|
|
error:
|
|
return -error;
|
|
}
|
|
|
|
struct afp_versions * pick_version(unsigned char *versions,
|
|
unsigned char requested)
|
|
{
|
|
/* Pick the right version number. This means either the
|
|
one requested or the last one. Set both the number and the
|
|
string. */
|
|
int i;
|
|
char version_num=0;
|
|
char found_version=0;
|
|
struct afp_versions * p;
|
|
char highest=0;
|
|
|
|
if (requested)
|
|
requested= min(requested,AFP_MAX_SUPPORTED_VERSION);
|
|
|
|
for (i=0;versions[i] && (i<SERVER_MAX_VERSIONS);i++) {
|
|
version_num=versions[i];
|
|
highest=max(highest,version_num);
|
|
if (versions[i]==requested) {
|
|
found_version=1;
|
|
break;
|
|
}
|
|
};
|
|
|
|
if (!found_version)
|
|
version_num=highest;
|
|
|
|
for (p=afp_versions;p->av_name;p++) {
|
|
if (p->av_number==version_num) {
|
|
return p;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int pick_uam(unsigned int uam2, unsigned int uam1)
|
|
{
|
|
|
|
int i;
|
|
for (i=15;i>=0;i--) {
|
|
if ( ((1<<i)) & (uam2 & uam1)) return (1<<i);
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int afp_list_volnames(struct afp_server * server, char * names, int max)
|
|
{
|
|
int len=0;
|
|
int i;
|
|
|
|
for (i=0;i<server->num_volumes;i++) {
|
|
if (i<server->num_volumes-1)
|
|
len+=snprintf(names+len,max-len,"%s, ",
|
|
server->volumes[i].volume_name_printable);
|
|
else
|
|
len+=snprintf(names+len,max-len,"%s",
|
|
server->volumes[i].volume_name_printable);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
|