X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=etc%2Fafpd%2Fstatus.c;h=745d349c5765768ce49960dfbd121312d55f97a0;hb=refs%2Ftags%2Fafter-ipv6;hp=f5acacaeb4c59245199c57b58048c4f05d278ca3;hpb=054df81bc269bfbf61b44424976a8eca799fce89;p=netatalk.git diff --git a/etc/afpd/status.c b/etc/afpd/status.c index f5acacae..745d349c 100644 --- a/etc/afpd/status.c +++ b/etc/afpd/status.c @@ -1,5 +1,5 @@ /* - * $Id: status.c,v 1.7 2002-02-06 21:58:50 jmarcus Exp $ + * $Id: status.c,v 1.27 2009-11-05 14:38:07 franklahm Exp $ * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -14,12 +14,13 @@ #include #include #include +#include #ifdef BSD4_4 #include -#ifndef USE_GETHOSTID +#ifndef HAVE_GETHOSTID #include -#endif /* USE_GETHOSTID */ +#endif /* HAVE_GETHOSTID */ #endif /* BSD4_4 */ #include @@ -28,14 +29,17 @@ #include #include #include +#include #include "globals.h" /* includes */ #include "status.h" #include "afp_config.h" #include "icon.h" +static size_t maxstatuslen = 0; + static void status_flags(char *data, const int notif, const int ipok, - const unsigned char passwdbits) + const unsigned char passwdbits, const int dirsrvcs _U_, int flags) { u_int16_t status; @@ -58,16 +62,23 @@ static void status_flags(char *data, const int notif, const int ipok, status |= AFPSRVRINFO_SRVNOTIFY; } status |= AFPSRVRINFO_FASTBOZO; + status |= AFPSRVRINFO_SRVRDIR; /* AFP 3.1 specs says we need to specify this, but may set the count to 0 */ + /* We don't set the UTF8 name flag here, we don't know whether we have enough space ... */ + + if (flags & OPTION_UUID) /* 05122008 FIXME: can we set AFPSRVRINFO_UUID here ? see AFPSRVRINFO_SRVRDIR*/ + status |= AFPSRVRINFO_UUID; + status = htons(status); memcpy(data + AFPSTATUS_FLAGOFF, &status, sizeof(status)); } -static int status_server(char *data, const char *server) +static int status_server(char *data, const char *server, const struct afp_options *options) { char *start = data; char *Obj, *Type, *Zone; + char buf[32]; u_int16_t status; - int len; + size_t len; /* make room for all offsets before server name */ data += AFPSTATUS_PRELEN; @@ -75,21 +86,33 @@ static int status_server(char *data, const char *server) /* extract the obj part of the server */ Obj = (char *) server; nbp_name(server, &Obj, &Type, &Zone); - len = strlen(Obj); - *data++ = len; - memcpy( data, Obj, len ); + if ((size_t)-1 == (len = convert_string( + options->unixcharset, options->maccharset, + Obj, -1, buf, sizeof(buf))) ) { + len = MIN(strlen(Obj), 31); + *data++ = len; + memcpy( data, Obj, len ); + LOG ( log_error, logtype_afpd, "Could not set servername, using fallback"); + } else { + *data++ = len; + memcpy( data, buf, len ); + } if ((len + 1) & 1) /* pad server name and length byte to even boundary */ len++; data += len; /* make room for signature and net address offset. save location of - * signature offset. + * signature offset. we're also making room for directory names offset + * and the utf-8 server name offset. * * NOTE: technically, we don't need to reserve space for the * signature and net address offsets if they're not going to be * used. as there are no offsets after them, it doesn't hurt to * have them specified though. so, we just do that to simplify * things. + * + * NOTE2: AFP3.1 Documentation states that the directory names offset + * is a required feature, even though it can be set to zero. */ len = data - start; status = htons(len + AFPSTATUS_POSTLEN); @@ -105,7 +128,7 @@ static void status_machine(char *data) #ifdef AFS const char *machine = "afs"; #else /* !AFS */ - const char *machine = "unix"; + const char *machine = "Netatalk"; #endif /* AFS */ memcpy(&status, start + AFPSTATUS_MACHOFF, sizeof(status)); @@ -118,16 +141,24 @@ static void status_machine(char *data) memcpy(start + AFPSTATUS_VERSOFF, &status, sizeof(status)); } +/* -------------------------------- + * it turns out that a server signature screws up separate + * servers running on the same machine. to work around that, + * i add in an increment. + * Not great, server signature are config dependent but well. + */ + +static int Id = 0; /* server signature is a 16-byte quantity */ static u_int16_t status_signature(char *data, int *servoffset, DSI *dsi, - const char *hostname) + const struct afp_options *options) { char *status; + char *usersign; int i; u_int16_t offset, sigoff; long hostid; - static int id = 0; #ifdef BSD4_4 int mib[2]; size_t len; @@ -142,22 +173,48 @@ static u_int16_t status_signature(char *data, int *servoffset, DSI *dsi, /* jump to server signature offset */ data += offset; + /* Signature type is user string */ + if (strncmp(options->signature, "user", 4) == 0) { + if (strlen(options->signature) <= 5) { + LOG(log_warning, logtype_afpd, "Signature %s id not valid. Switching back to hostid.", + options->signature); + goto server_signature_hostid; + } + usersign = options->signature + 5; + if (strlen(usersign) < 3) + LOG(log_warning, logtype_afpd, "Signature %s is very short !", + options->signature); + + memset(data, 0, 16); + strncpy(data, usersign, 16); + data += 16; + goto server_signature_done; + } /* signature = user */ + + /* If signature type is a standard hostid... */ +server_signature_hostid: /* 16-byte signature consists of copies of the hostid */ -#if defined(BSD4_4) && defined(USE_GETHOSTID) +#if defined(BSD4_4) && !defined(HAVE_GETHOSTID) mib[0] = CTL_KERN; mib[1] = KERN_HOSTID; len = sizeof(hostid); sysctl(mib, 2, &hostid, &len, NULL, 0); -#else /* BSD4_4 && USE_GETHOSTID */ +#else /* BSD4_4 && !HAVE_GETHOSTID */ hostid = gethostid(); -#endif /* BSD4_4 && USE_GETHOSTID */ +#endif /* BSD4_4 && !HAVE_GETHOSTID */ if (!hostid) { - if (dsi) - hostid = dsi->server.sin_addr.s_addr; - else { + if (dsi) { + if (dsi->server.ss_family == AF_INET) { /* IPv4 */ + hostid = ((struct sockaddr_in *)(&dsi->server))->sin_addr.s_addr; + } else { /* IPv6 */ + struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&dsi->server; + /* Use the last "sizeof(long) bytes of the IPv6 addr */ + memcpy(&hostid, sa6->sin6_addr.s6_addr + (16 - sizeof(long)), sizeof(long)); + } + } else { struct hostent *host; - if ((host = gethostbyname(hostname))) + if ((host = gethostbyname(options->hostname))) hostid = ((struct in_addr *) host->h_addr)->s_addr; } } @@ -165,13 +222,14 @@ static u_int16_t status_signature(char *data, int *servoffset, DSI *dsi, /* it turns out that a server signature screws up separate * servers running on the same machine. to work around that, * i add in an increment */ - hostid += id; - id++; + hostid += Id; + Id++; for (i = 0; i < 16; i += sizeof(hostid)) { memcpy(data, &hostid, sizeof(hostid)); data += sizeof(hostid); - } + } +server_signature_done: /* calculate net address offset */ *servoffset += sizeof(offset); offset = htons(data - status); @@ -179,17 +237,18 @@ static u_int16_t status_signature(char *data, int *servoffset, DSI *dsi, return sigoff; } -static int status_netaddress(char *data, const int servoffset, +static size_t status_netaddress(char *data, int *servoffset, const ASP asp, const DSI *dsi, - const char *fqdn) + const struct afp_options *options) { char *begin; u_int16_t offset; + size_t addresses_len = 0; begin = data; /* get net address offset */ - memcpy(&offset, data + servoffset, sizeof(offset)); + memcpy(&offset, data + *servoffset, sizeof(offset)); data += ntohs(offset); /* format: @@ -203,36 +262,77 @@ static int status_netaddress(char *data, const int servoffset, /* number of addresses. this currently screws up if we have a dsi connection, but we don't have the ip address. to get around this, we turn off the status flag for tcp/ip. */ - *data++ = (fqdn ? 1 : 0) + (dsi ? 1 : 0) + (asp ? 1 : 0); - - /* handle DNS names */ - if (fqdn) { - int len = strlen(fqdn) + 2; - *data++ = len; - *data++ = 0x04; - memcpy(data, fqdn, len); - data += len; - } + *data++ = ((options->fqdn && dsi)? 1 : 0) + (dsi ? 1 : 0) + (asp ? 1 : 0) + + (((options->flags & OPTION_ANNOUNCESSH) && options->fqdn && dsi)? 1 : 0); /* ip address */ if (dsi) { - const struct sockaddr_in *inaddr = &dsi->server; - - if (inaddr->sin_port == htons(DSI_AFPOVERTCP_PORT)) { - *data++ = 6; /* length */ - *data++ = 0x01; /* basic ip address */ - memcpy(data, &inaddr->sin_addr.s_addr, - sizeof(inaddr->sin_addr.s_addr)); - data += sizeof(inaddr->sin_addr.s_addr); - } else { - /* ip address + port */ - *data++ = 8; - *data++ = 0x02; /* ip address with port */ - memcpy(data, &inaddr->sin_addr.s_addr, - sizeof(inaddr->sin_addr.s_addr)); - data += sizeof(inaddr->sin_addr.s_addr); - memcpy(data, &inaddr->sin_port, sizeof(inaddr->sin_port)); - data += sizeof(inaddr->sin_port); + if (dsi->server.ss_family == AF_INET) { /* IPv4 */ + const struct sockaddr_in *inaddr = (struct sockaddr_in *)&dsi->server; + if (inaddr->sin_port == htons(DSI_AFPOVERTCP_PORT)) { + *data++ = 6; /* length */ + *data++ = 0x01; /* basic ip address */ + memcpy(data, &inaddr->sin_addr.s_addr, + sizeof(inaddr->sin_addr.s_addr)); + data += sizeof(inaddr->sin_addr.s_addr); + addresses_len += 7; + } else { + /* ip address + port */ + *data++ = 8; + *data++ = 0x02; /* ip address with port */ + memcpy(data, &inaddr->sin_addr.s_addr, + sizeof(inaddr->sin_addr.s_addr)); + data += sizeof(inaddr->sin_addr.s_addr); + memcpy(data, &inaddr->sin_port, sizeof(inaddr->sin_port)); + data += sizeof(inaddr->sin_port); + addresses_len += 9; + } + } else { /* IPv6 */ + const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&dsi->server; + if (sa6->sin6_port == htons(DSI_AFPOVERTCP_PORT)) { + *data++ = 18; /* length */ + *data++ = 6; /* type */ + memcpy(data, &sa6->sin6_addr.s6_addr, sizeof(sa6->sin6_addr.s6_addr)); + data += sizeof(sa6->sin6_addr.s6_addr); + addresses_len += 19; + } else { + /* ip address + port */ + *data++ = 20; /* length */ + *data++ = 7; /* type*/ + memcpy(data, &sa6->sin6_addr.s6_addr, sizeof(sa6->sin6_addr.s6_addr)); + data += sizeof(sa6->sin6_addr.s6_addr); + memcpy(data, &sa6->sin6_port, sizeof(sa6->sin6_port)); + data += sizeof(sa6->sin6_port); + addresses_len += 21; + } + + } + } + + /* handle DNS names */ + if (options->fqdn && dsi) { + size_t len = strlen(options->fqdn); + if ( len + 2 + addresses_len < maxstatuslen - offset) { + *data++ = len +2; + *data++ = 0x04; + memcpy(data, options->fqdn, len); + data += len; + addresses_len += len+2; + } + + /* Annouce support for SSH tunneled AFP session, + * this feature is available since 10.3.2. + * According to the specs (AFP 3.1 p.225) this should + * be an IP+Port style value, but it only works with + * a FQDN. OSX Server uses FQDN as well. + */ + if ( len + 2 + addresses_len < maxstatuslen - offset) { + if (options->flags & OPTION_ANNOUNCESSH) { + *data++ = len +2; + *data++ = 0x05; + memcpy(data, options->fqdn, len); + data += len; + } } } @@ -253,10 +353,124 @@ static int status_netaddress(char *data, const int servoffset, } #endif /* ! NO_DDP */ + /* calculate/store Directory Services Names offset */ + offset = htons(data - begin); + *servoffset += sizeof(offset); + memcpy(begin + *servoffset, &offset, sizeof(offset)); + /* return length of buffer */ return (data - begin); } +static size_t status_directorynames(char *data, int *diroffset, + const DSI *dsi _U_, + const struct afp_options *options) +{ + char *begin = data; + u_int16_t offset; + memcpy(&offset, data + *diroffset, sizeof(offset)); + offset = ntohs(offset); + data += offset; + + /* I can not find documentation of any other purpose for the + * DirectoryNames field. + */ + /* + * Try to synthesize a principal: + * service '/' fqdn '@' realm + */ + if (options->k5service && options->k5realm && options->fqdn) { + /* should k5princ be utf8 encoded? */ + size_t len; + char *p = strchr( options->fqdn, ':' ); + if (p) + *p = '\0'; + len = strlen( options->k5service ) + + strlen( options->fqdn ) + + strlen( options->k5realm ); + len+=2; /* '/' and '@' */ + if ( len > 255 || len+2 > maxstatuslen - offset) { + *data++ = 0; + LOG ( log_error, logtype_afpd, "status: could not set directory service list, no more room"); + } + else { + *data++ = 1; /* DirectoryNamesCount */ + *data++ = len; + snprintf( data, len + 1, "%s/%s@%s", options->k5service, + options->fqdn, options->k5realm ); + data += len; + if (p) + *p = ':'; + } + } else { + *data++ = 0; + } + + /* Calculate and store offset for UTF8ServerName */ + *diroffset += sizeof(u_int16_t); + offset = htons(data - begin); + memcpy(begin + *diroffset, &offset, sizeof(u_int16_t)); + + /* return length of buffer */ + return (data - begin); +} + +static size_t status_utf8servername(char *data, int *nameoffset, + const DSI *dsi _U_, + const struct afp_options *options) +{ + char *Obj, *Type, *Zone; + u_int16_t namelen; + size_t len; + char *begin = data; + u_int16_t offset, status; + + memcpy(&offset, data + *nameoffset, sizeof(offset)); + offset = ntohs(offset); + data += offset; + + /* FIXME: + * What is the valid character range for an nbpname? + * + * Apple's server likes to use the non-qualified hostname + * This obviously won't work very well if multiple servers are running + * on the box. + */ + + /* extract the obj part of the server */ + Obj = (char *) (options->server ? options->server : options->hostname); + nbp_name(options->server ? options->server : options->hostname, &Obj, &Type, &Zone); + + if ((size_t) -1 == (len = convert_string ( + options->unixcharset, CH_UTF8_MAC, + Obj, -1, data+sizeof(namelen), maxstatuslen-offset )) ) { + LOG ( log_error, logtype_afpd, "Could not set utf8 servername"); + + /* set offset to 0 */ + memset(begin + *nameoffset, 0, sizeof(offset)); + data = begin + offset; + } + else { + namelen = htons(len); + memcpy( data, &namelen, sizeof(namelen)); + data += sizeof(namelen); + data += len; + offset = htons(offset); + memcpy(begin + *nameoffset, &offset, sizeof(u_int16_t)); + + /* Now set the flag ... */ + memcpy(&status, begin + AFPSTATUS_FLAGOFF, sizeof(status)); + status = ntohs(status); + status |= AFPSRVRINFO_SRVUTF8; + status = htons(status); + memcpy(begin + AFPSTATUS_FLAGOFF, &status, sizeof(status)); + } + + /* return length of buffer */ + return (data - begin); + +} + /* returns actual offset to signature */ static void status_icon(char *data, const unsigned char *icondata, const int iconlen, const int sigoffset) @@ -281,27 +495,52 @@ static void status_icon(char *data, const unsigned char *icondata, memcpy(sigdata, &ret, sizeof(ret)); } +/* --------------------- +*/ +void status_reset(void) +{ + Id = 0; +} + + +/* --------------------- + */ void status_init(AFPConfig *aspconfig, AFPConfig *dsiconfig, const struct afp_options *options) { ASP asp; DSI *dsi; - char *status; - int c, sigoff; + char *status = NULL; + size_t statuslen; + int c, sigoff, ipok; if (!(aspconfig || dsiconfig) || !options) return; if (aspconfig) { - asp = aspconfig->obj.handle; status = aspconfig->status; + maxstatuslen=sizeof(aspconfig->status); + asp = aspconfig->obj.handle; } else asp = NULL; - + if (dsiconfig) { + status = dsiconfig->status; + maxstatuslen=sizeof(dsiconfig->status); dsi = dsiconfig->obj.handle; - if (!aspconfig) - status = dsiconfig->status; + if (dsi->server.ss_family == AF_INET) { /* IPv4 */ + const struct sockaddr_in *sa4 = (struct sockaddr_in *)&dsi->server; + ipok = sa4->sin_addr.s_addr ? 1 : 0; + } else { /* IPv6 */ + const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&dsi->server; + ipok = 0; + for (int i=0; i<16; i++) { + if (sa6->sin6_addr.s6_addr[i]) { + ipok = 1; + break; + } + } + } } else dsi = NULL; @@ -325,12 +564,15 @@ void status_init(AFPConfig *aspconfig, AFPConfig *dsiconfig, * (16-bytes), network addresses, volume icon/mask */ - status_flags(status, options->server_notif, options->fqdn || - (dsiconfig && dsi->server.sin_addr.s_addr), - options->passwdbits); + status_flags(status, + options->server_notif, + (options->fqdn || ipok), + options->passwdbits, + (options->k5service && options->k5realm && options->fqdn), + options->flags); /* returns offset to signature offset */ c = status_server(status, options->server ? options->server : - options->hostname); + options->hostname, options); status_machine(status); status_versions(status); status_uams(status, options->uamlist); @@ -339,40 +581,40 @@ void status_init(AFPConfig *aspconfig, AFPConfig *dsiconfig, else status_icon(status, apple_atalk_icon, sizeof(apple_atalk_icon), c); - sigoff = status_signature(status, &c, dsi, options->hostname); + sigoff = status_signature(status, &c, dsi, options); + /* c now contains the offset where the netaddress offset lives */ + + status_netaddress(status, &c, asp, dsi, options); + /* c now contains the offset where the Directory Names Count offset lives */ - /* returns length */ - c = status_netaddress(status, c, asp, dsi, options->fqdn); + statuslen = status_directorynames(status, &c, dsi, options); + /* c now contains the offset where the UTF-8 ServerName offset lives */ + if ( statuslen < maxstatuslen) + statuslen = status_utf8servername(status, &c, dsi, options); #ifndef NO_DDP if (aspconfig) { - asp_setstatus(asp, status, c); + if (dsiconfig) /* status is dsiconfig->status */ + memcpy(aspconfig->status, status, statuslen); + asp_setstatus(asp, status, statuslen); aspconfig->signature = status + sigoff; - aspconfig->statuslen = c; + aspconfig->statuslen = statuslen; } #endif /* ! NO_DDP */ if (dsiconfig) { - if (aspconfig) { /* copy to dsiconfig */ - memcpy(dsiconfig->status, status, ATP_MAXDATA); - status = dsiconfig->status; - } - if ((options->flags & OPTION_CUSTOMICON) == 0) { status_icon(status, apple_tcp_icon, sizeof(apple_tcp_icon), 0); } - dsi_setstatus(dsi, status, c); + dsi_setstatus(dsi, status, statuslen); dsiconfig->signature = status + sigoff; - dsiconfig->statuslen = c; + dsiconfig->statuslen = statuslen; } } /* this is the same as asp/dsi_getstatus */ -int afp_getsrvrinfo(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +int afp_getsrvrinfo(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) { AFPConfig *config = obj->config;