]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/status.c
745d349c5765768ce49960dfbd121312d55f97a0
[netatalk.git] / etc / afpd / status.c
1 /*
2  * $Id: status.c,v 1.27 2009-11-05 14:38:07 franklahm Exp $
3  *
4  * Copyright (c) 1990,1993 Regents of The University of Michigan.
5  * All Rights Reserved.  See COPYRIGHT.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif /* HAVE_CONFIG_H */
11
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <atalk/logger.h>
18
19 #ifdef BSD4_4
20 #include <sys/param.h>
21 #ifndef HAVE_GETHOSTID
22 #include <sys/sysctl.h>
23 #endif /* HAVE_GETHOSTID */
24 #endif /* BSD4_4 */
25
26 #include <netatalk/at.h>
27 #include <netatalk/endian.h>
28 #include <atalk/dsi.h>
29 #include <atalk/atp.h>
30 #include <atalk/asp.h>
31 #include <atalk/nbp.h>
32 #include <atalk/unicode.h>
33
34 #include "globals.h"  /* includes <netdb.h> */
35 #include "status.h"
36 #include "afp_config.h"
37 #include "icon.h"
38
39 static   size_t maxstatuslen = 0;
40
41 static void status_flags(char *data, const int notif, const int ipok,
42                          const unsigned char passwdbits, const int dirsrvcs _U_, int flags)
43 {
44     u_int16_t           status;
45
46     status = AFPSRVRINFO_COPY;
47     if (passwdbits & PASSWD_SET) /* some uams may not allow this. */
48         status |= AFPSRVRINFO_PASSWD;
49     if (passwdbits & PASSWD_NOSAVE)
50         status |= AFPSRVRINFO_NOSAVEPASSWD;
51     status |= AFPSRVRINFO_SRVSIGNATURE;
52     /* only advertise tcp/ip if we have a valid address */
53     if (ipok)
54         status |= AFPSRVRINFO_TCPIP;
55     status |= AFPSRVRINFO_SRVMSGS;
56     /* Allow the user to decide if we should support server notifications.
57      * With this turned off, the clients will poll for directory changes every
58      * 10 seconds.  This might be too costly to network resources, so make
59      * this an optional thing.  Default will be to _not_ support server
60      * notifications. */
61     if (notif) {
62         status |= AFPSRVRINFO_SRVNOTIFY;
63     }
64     status |= AFPSRVRINFO_FASTBOZO;
65     status |= AFPSRVRINFO_SRVRDIR; /* AFP 3.1 specs says we need to specify this, but may set the count to 0 */
66     /* We don't set the UTF8 name flag here, we don't know whether we have enough space ... */
67
68     if (flags & OPTION_UUID)    /* 05122008 FIXME: can we set AFPSRVRINFO_UUID here ? see AFPSRVRINFO_SRVRDIR*/
69         status |= AFPSRVRINFO_UUID;
70
71     status = htons(status);
72     memcpy(data + AFPSTATUS_FLAGOFF, &status, sizeof(status));
73 }
74
75 static int status_server(char *data, const char *server, const struct afp_options *options)
76 {
77     char                *start = data;
78     char                *Obj, *Type, *Zone;
79     char                buf[32];
80     u_int16_t           status;
81     size_t              len;
82
83     /* make room for all offsets before server name */
84     data += AFPSTATUS_PRELEN;
85
86     /* extract the obj part of the server */
87     Obj = (char *) server;
88     nbp_name(server, &Obj, &Type, &Zone);
89     if ((size_t)-1 == (len = convert_string( 
90                         options->unixcharset, options->maccharset, 
91                         Obj, -1, buf, sizeof(buf))) ) {
92         len = MIN(strlen(Obj), 31);
93         *data++ = len;
94         memcpy( data, Obj, len );
95         LOG ( log_error, logtype_afpd, "Could not set servername, using fallback");
96     } else {
97         *data++ = len;
98         memcpy( data, buf, len );
99     }
100     if ((len + 1) & 1) /* pad server name and length byte to even boundary */
101         len++;
102     data += len;
103
104     /* make room for signature and net address offset. save location of
105      * signature offset. we're also making room for directory names offset
106      * and the utf-8 server name offset.
107      *
108      * NOTE: technically, we don't need to reserve space for the
109      * signature and net address offsets if they're not going to be
110      * used. as there are no offsets after them, it doesn't hurt to
111      * have them specified though. so, we just do that to simplify
112      * things.  
113      *
114      * NOTE2: AFP3.1 Documentation states that the directory names offset
115      * is a required feature, even though it can be set to zero.
116      */
117     len = data - start;
118     status = htons(len + AFPSTATUS_POSTLEN);
119     memcpy(start + AFPSTATUS_MACHOFF, &status, sizeof(status));
120     return len; /* return the offset to beginning of signature offset */
121 }
122
123 static void status_machine(char *data)
124 {
125     char                *start = data;
126     u_int16_t           status;
127     int                 len;
128 #ifdef AFS
129     const char          *machine = "afs";
130 #else /* !AFS */
131     const char          *machine = "Netatalk";
132 #endif /* AFS */
133
134     memcpy(&status, start + AFPSTATUS_MACHOFF, sizeof(status));
135     data += ntohs( status );
136     len = strlen( machine );
137     *data++ = len;
138     memcpy( data, machine, len );
139     data += len;
140     status = htons(data - start);
141     memcpy(start + AFPSTATUS_VERSOFF, &status, sizeof(status));
142 }
143
144 /* -------------------------------- 
145  * it turns out that a server signature screws up separate
146  * servers running on the same machine. to work around that, 
147  * i add in an increment.
148  * Not great, server signature are config dependent but well.
149  */
150  
151 static int           Id = 0;
152
153 /* server signature is a 16-byte quantity */
154 static u_int16_t status_signature(char *data, int *servoffset, DSI *dsi,
155                                   const struct afp_options *options)
156 {
157     char                 *status;
158     char                 *usersign;
159     int                  i;
160     u_int16_t            offset, sigoff;
161     long                 hostid;
162 #ifdef BSD4_4
163     int                  mib[2];
164     size_t               len;
165 #endif /* BSD4_4 */
166
167     status = data;
168
169     /* get server signature offset */
170     memcpy(&offset, data + *servoffset, sizeof(offset));
171     sigoff = offset = ntohs(offset);
172
173     /* jump to server signature offset */
174     data += offset;
175
176     /* Signature type is user string */
177     if (strncmp(options->signature, "user", 4) == 0) {
178         if (strlen(options->signature) <= 5) {
179             LOG(log_warning, logtype_afpd, "Signature %s id not valid. Switching back to hostid.",
180                             options->signature);
181             goto server_signature_hostid;
182     }
183     usersign = options->signature + 5;
184         if (strlen(usersign) < 3) 
185             LOG(log_warning, logtype_afpd, "Signature %s is very short !", 
186                             options->signature);
187     
188         memset(data, 0, 16);
189         strncpy(data, usersign, 16);
190         data += 16;
191         goto server_signature_done;
192     } /* signature = user */
193
194     /* If signature type is a standard hostid... */
195 server_signature_hostid:
196     /* 16-byte signature consists of copies of the hostid */
197 #if defined(BSD4_4) && !defined(HAVE_GETHOSTID)
198     mib[0] = CTL_KERN;
199     mib[1] = KERN_HOSTID;
200     len = sizeof(hostid);
201     sysctl(mib, 2, &hostid, &len, NULL, 0);
202 #else /* BSD4_4 && !HAVE_GETHOSTID */
203     hostid = gethostid();
204 #endif /* BSD4_4 && !HAVE_GETHOSTID */
205     if (!hostid) {
206         if (dsi) {
207             if (dsi->server.ss_family == AF_INET) { /* IPv4 */
208                 hostid = ((struct sockaddr_in *)(&dsi->server))->sin_addr.s_addr;
209             } else {                                /* IPv6 */
210                 struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&dsi->server;
211                 /* Use the last "sizeof(long) bytes of the IPv6 addr */
212                 memcpy(&hostid, sa6->sin6_addr.s6_addr + (16 - sizeof(long)), sizeof(long));
213             }
214         } else {
215             struct hostent *host;
216
217             if ((host = gethostbyname(options->hostname)))
218                 hostid = ((struct in_addr *) host->h_addr)->s_addr;
219         }
220     }
221
222     /* it turns out that a server signature screws up separate
223      * servers running on the same machine. to work around that, 
224      * i add in an increment */
225     hostid += Id;
226     Id++;
227     for (i = 0; i < 16; i += sizeof(hostid)) {
228         memcpy(data, &hostid, sizeof(hostid));
229         data += sizeof(hostid);
230      }
231
232 server_signature_done:
233     /* calculate net address offset */
234     *servoffset += sizeof(offset);
235     offset = htons(data - status);
236     memcpy(status + *servoffset, &offset, sizeof(offset));
237     return sigoff;
238 }
239
240 static size_t status_netaddress(char *data, int *servoffset,
241                              const ASP asp, const DSI *dsi,
242                              const struct afp_options *options)
243 {
244     char               *begin;
245     u_int16_t          offset;
246     size_t             addresses_len = 0;
247
248     begin = data;
249
250     /* get net address offset */
251     memcpy(&offset, data + *servoffset, sizeof(offset));
252     data += ntohs(offset);
253
254     /* format:
255        Address count (byte) 
256        len (byte = sizeof(length + address type + address) 
257        address type (byte, ip address = 0x01, ip + port = 0x02,
258                            ddp address = 0x03, fqdn = 0x04) 
259        address (up to 254 bytes, ip = 4, ip + port = 6, ddp = 4)
260        */
261
262     /* number of addresses. this currently screws up if we have a dsi
263        connection, but we don't have the ip address. to get around this,
264        we turn off the status flag for tcp/ip. */
265     *data++ = ((options->fqdn && dsi)? 1 : 0) + (dsi ? 1 : 0) + (asp ? 1 : 0) +
266               (((options->flags & OPTION_ANNOUNCESSH) && options->fqdn && dsi)? 1 : 0);
267
268     /* ip address */
269     if (dsi) {
270         if (dsi->server.ss_family == AF_INET) { /* IPv4 */
271             const struct sockaddr_in *inaddr = (struct sockaddr_in *)&dsi->server;
272             if (inaddr->sin_port == htons(DSI_AFPOVERTCP_PORT)) {
273                 *data++ = 6; /* length */
274                 *data++ = 0x01; /* basic ip address */
275                 memcpy(data, &inaddr->sin_addr.s_addr,
276                        sizeof(inaddr->sin_addr.s_addr));
277                 data += sizeof(inaddr->sin_addr.s_addr);
278                 addresses_len += 7;
279             } else {
280                 /* ip address + port */
281                 *data++ = 8;
282                 *data++ = 0x02; /* ip address with port */
283                 memcpy(data, &inaddr->sin_addr.s_addr,
284                        sizeof(inaddr->sin_addr.s_addr));
285                 data += sizeof(inaddr->sin_addr.s_addr);
286                 memcpy(data, &inaddr->sin_port, sizeof(inaddr->sin_port));
287                 data += sizeof(inaddr->sin_port);
288                 addresses_len += 9;
289             }
290         } else { /* IPv6 */
291             const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&dsi->server;
292             if (sa6->sin6_port == htons(DSI_AFPOVERTCP_PORT)) {
293                 *data++ = 18; /* length */
294                 *data++ = 6; /* type */
295                 memcpy(data, &sa6->sin6_addr.s6_addr, sizeof(sa6->sin6_addr.s6_addr));
296                 data += sizeof(sa6->sin6_addr.s6_addr);
297                 addresses_len += 19;
298             } else {
299                 /* ip address + port */
300                 *data++ = 20; /* length */
301                 *data++ = 7; /* type*/
302                 memcpy(data, &sa6->sin6_addr.s6_addr, sizeof(sa6->sin6_addr.s6_addr));
303                 data += sizeof(sa6->sin6_addr.s6_addr);
304                 memcpy(data, &sa6->sin6_port, sizeof(sa6->sin6_port));
305                 data += sizeof(sa6->sin6_port);
306                 addresses_len += 21;
307             }
308
309         }
310     }
311
312     /* handle DNS names */
313     if (options->fqdn && dsi) {
314         size_t len = strlen(options->fqdn);
315         if ( len + 2 + addresses_len < maxstatuslen - offset) {
316             *data++ = len +2;
317             *data++ = 0x04;
318             memcpy(data, options->fqdn, len);
319             data += len;
320             addresses_len += len+2;
321         }
322
323         /* Annouce support for SSH tunneled AFP session, 
324          * this feature is available since 10.3.2.
325          * According to the specs (AFP 3.1 p.225) this should
326          * be an IP+Port style value, but it only works with 
327          * a FQDN. OSX Server uses FQDN as well.
328          */
329         if ( len + 2 + addresses_len < maxstatuslen - offset) {
330             if (options->flags & OPTION_ANNOUNCESSH) {
331                 *data++ = len +2;
332                 *data++ = 0x05;
333                 memcpy(data, options->fqdn, len);
334                 data += len;
335             }
336         }
337     }
338
339 #ifndef NO_DDP
340     if (asp) {
341         const struct sockaddr_at *ddpaddr = atp_sockaddr(asp->asp_atp);
342
343         /* ddp address */
344         *data++ = 6;
345         *data++ = 0x03; /* ddp address */
346         memcpy(data, &ddpaddr->sat_addr.s_net, sizeof(ddpaddr->sat_addr.s_net));
347         data += sizeof(ddpaddr->sat_addr.s_net);
348         memcpy(data, &ddpaddr->sat_addr.s_node,
349                sizeof(ddpaddr->sat_addr.s_node));
350         data += sizeof(ddpaddr->sat_addr.s_node);
351         memcpy(data, &ddpaddr->sat_port, sizeof(ddpaddr->sat_port));
352         data += sizeof(ddpaddr->sat_port);
353     }
354 #endif /* ! NO_DDP */
355
356     /* calculate/store Directory Services Names offset */
357     offset = htons(data - begin); 
358     *servoffset += sizeof(offset);
359     memcpy(begin + *servoffset, &offset, sizeof(offset));
360
361     /* return length of buffer */
362     return (data - begin);
363 }
364
365 static size_t status_directorynames(char *data, int *diroffset, 
366                                  const DSI *dsi _U_, 
367                                  const struct afp_options *options)
368 {
369     char *begin = data;
370     u_int16_t offset;
371     memcpy(&offset, data + *diroffset, sizeof(offset));
372     offset = ntohs(offset);
373     data += offset;
374
375     /* I can not find documentation of any other purpose for the
376      * DirectoryNames field.
377      */
378     /*
379      * Try to synthesize a principal:
380      * service '/' fqdn '@' realm
381      */
382     if (options->k5service && options->k5realm && options->fqdn) {
383         /* should k5princ be utf8 encoded? */
384         size_t len;
385         char *p = strchr( options->fqdn, ':' );
386         if (p) 
387             *p = '\0';
388         len = strlen( options->k5service ) 
389                         + strlen( options->fqdn )
390                         + strlen( options->k5realm );
391         len+=2; /* '/' and '@' */
392         if ( len > 255 || len+2 > maxstatuslen - offset) {
393             *data++ = 0;
394             LOG ( log_error, logtype_afpd, "status: could not set directory service list, no more room");
395         }        
396         else {
397             *data++ = 1; /* DirectoryNamesCount */
398             *data++ = len;
399             snprintf( data, len + 1, "%s/%s@%s", options->k5service,
400                                 options->fqdn, options->k5realm );
401             data += len;
402             if (p)
403                 *p = ':';
404        }
405     } else {
406         *data++ = 0;
407     }
408
409     /* Calculate and store offset for UTF8ServerName */
410     *diroffset += sizeof(u_int16_t);
411     offset = htons(data - begin);
412     memcpy(begin + *diroffset, &offset, sizeof(u_int16_t));
413
414     /* return length of buffer */
415     return (data - begin);
416 }
417
418 static size_t status_utf8servername(char *data, int *nameoffset,
419                                  const DSI *dsi _U_,
420                                  const struct afp_options *options)
421 {
422     char *Obj, *Type, *Zone;
423     u_int16_t namelen;
424     size_t len;
425     char *begin = data;
426     u_int16_t offset, status;
427
428     memcpy(&offset, data + *nameoffset, sizeof(offset));
429     offset = ntohs(offset);
430     data += offset;
431
432     /* FIXME:
433      * What is the valid character range for an nbpname?
434      *
435      * Apple's server likes to use the non-qualified hostname
436      * This obviously won't work very well if multiple servers are running
437      * on the box.
438      */
439
440     /* extract the obj part of the server */
441     Obj = (char *) (options->server ? options->server : options->hostname);
442     nbp_name(options->server ? options->server : options->hostname, &Obj, &Type, &Zone);
443
444     if ((size_t) -1 == (len = convert_string (
445                                         options->unixcharset, CH_UTF8_MAC, 
446                                         Obj, -1, data+sizeof(namelen), maxstatuslen-offset )) ) {
447         LOG ( log_error, logtype_afpd, "Could not set utf8 servername");
448
449         /* set offset to 0 */
450         memset(begin + *nameoffset, 0, sizeof(offset));
451         data = begin + offset;
452     }
453     else {
454         namelen = htons(len);
455         memcpy( data, &namelen, sizeof(namelen));
456         data += sizeof(namelen);
457         data += len;
458         offset = htons(offset);
459         memcpy(begin + *nameoffset, &offset, sizeof(u_int16_t));
460         
461         /* Now set the flag ... */
462         memcpy(&status, begin + AFPSTATUS_FLAGOFF, sizeof(status));
463         status = ntohs(status);
464         status |= AFPSRVRINFO_SRVUTF8;
465         status = htons(status);
466         memcpy(begin + AFPSTATUS_FLAGOFF, &status, sizeof(status));
467     }
468
469     /* return length of buffer */
470     return (data - begin);
471
472 }
473
474 /* returns actual offset to signature */
475 static void status_icon(char *data, const unsigned char *icondata,
476                         const int iconlen, const int sigoffset)
477 {
478     char                *start = data;
479     char                *sigdata = data + sigoffset;
480     u_int16_t           ret, status;
481
482     memcpy(&status, start + AFPSTATUS_ICONOFF, sizeof(status));
483     if ( icondata == NULL ) {
484         ret = status;
485         memset(start + AFPSTATUS_ICONOFF, 0, sizeof(status));
486     } else {
487         data += ntohs( status );
488         memcpy( data, icondata, iconlen);
489         data += iconlen;
490         ret = htons(data - start);
491     }
492
493     /* put in signature offset */
494     if (sigoffset)
495         memcpy(sigdata, &ret, sizeof(ret));
496 }
497
498 /* ---------------------
499 */
500 void status_reset(void)
501 {
502     Id = 0;
503 }
504
505
506 /* ---------------------
507  */
508 void status_init(AFPConfig *aspconfig, AFPConfig *dsiconfig,
509                  const struct afp_options *options)
510 {
511     ASP asp;
512     DSI *dsi;
513     char *status = NULL;
514     size_t statuslen;
515     int c, sigoff, ipok;
516
517     if (!(aspconfig || dsiconfig) || !options)
518         return;
519
520     if (aspconfig) {
521         status = aspconfig->status;
522         maxstatuslen=sizeof(aspconfig->status);
523         asp = aspconfig->obj.handle;
524     } else
525         asp = NULL;
526         
527     if (dsiconfig) {
528         status = dsiconfig->status;
529         maxstatuslen=sizeof(dsiconfig->status);
530         dsi = dsiconfig->obj.handle;
531         if (dsi->server.ss_family == AF_INET) { /* IPv4 */
532             const struct sockaddr_in *sa4 = (struct sockaddr_in *)&dsi->server;
533             ipok = sa4->sin_addr.s_addr ? 1 : 0;
534         } else { /* IPv6 */
535             const struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&dsi->server;
536             ipok = 0;
537             for (int i=0; i<16; i++) {
538                 if (sa6->sin6_addr.s6_addr[i]) {
539                     ipok = 1;
540                     break;
541                 }
542             }
543         }
544     } else
545         dsi = NULL;
546
547     /*
548      * These routines must be called in order -- earlier calls
549      * set the offsets for later calls.
550      *
551      * using structs is a bad idea, but that's what the original code
552      * does. solaris, in fact, will segfault with them. so, now
553      * we just use the powers of #defines and memcpy.
554      *
555      * reply block layout (offsets are 16-bit quantities):
556      * machine type offset -> AFP version count offset ->
557      * UAM count offset -> vol icon/mask offset -> flags ->
558      *
559      * server name [padded to even boundary] -> signature offset ->
560      * network address offset ->
561      *
562      * at the appropriate offsets:
563      * machine type, afp versions, uams, server signature
564      * (16-bytes), network addresses, volume icon/mask 
565      */
566
567     status_flags(status,
568                  options->server_notif,
569                  (options->fqdn || ipok),
570                  options->passwdbits, 
571                  (options->k5service && options->k5realm && options->fqdn),
572                  options->flags);
573     /* returns offset to signature offset */
574     c = status_server(status, options->server ? options->server :
575                       options->hostname, options);
576     status_machine(status);
577     status_versions(status);
578     status_uams(status, options->uamlist);
579     if (options->flags & OPTION_CUSTOMICON)
580         status_icon(status, icon, sizeof(icon), c);
581     else
582         status_icon(status, apple_atalk_icon, sizeof(apple_atalk_icon), c);
583
584     sigoff = status_signature(status, &c, dsi, options);
585     /* c now contains the offset where the netaddress offset lives */
586
587     status_netaddress(status, &c, asp, dsi, options);
588     /* c now contains the offset where the Directory Names Count offset lives */
589
590     statuslen = status_directorynames(status, &c, dsi, options);
591     /* c now contains the offset where the UTF-8 ServerName offset lives */
592
593     if ( statuslen < maxstatuslen) 
594         statuslen = status_utf8servername(status, &c, dsi, options);
595
596 #ifndef NO_DDP
597     if (aspconfig) {
598         if (dsiconfig) /* status is dsiconfig->status */
599             memcpy(aspconfig->status, status, statuslen);
600         asp_setstatus(asp, status, statuslen);
601         aspconfig->signature = status + sigoff;
602         aspconfig->statuslen = statuslen;
603     }
604 #endif /* ! NO_DDP */
605
606     if (dsiconfig) {
607         if ((options->flags & OPTION_CUSTOMICON) == 0) {
608             status_icon(status, apple_tcp_icon, sizeof(apple_tcp_icon), 0);
609         }
610         dsi_setstatus(dsi, status, statuslen);
611         dsiconfig->signature = status + sigoff;
612         dsiconfig->statuslen = statuslen;
613     }
614 }
615
616 /* this is the same as asp/dsi_getstatus */
617 int afp_getsrvrinfo(AFPObj *obj, char *ibuf _U_, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
618 {
619     AFPConfig *config = obj->config;
620
621     memcpy(rbuf, config->status, config->statuslen);
622     *rbuflen = config->statuslen;
623     return AFP_OK;
624 }