]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
Merge from 2-1
[netatalk.git] / etc / afpd / afp_config.c
1 /*
2  * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
3  * All Rights Reserved.  See COPYRIGHT.
4  */
5
6 #ifdef HAVE_CONFIG_H
7 #include "config.h"
8 #endif /* HAVE_CONFIG_H */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <errno.h>
14
15 /* STDC check */
16 #if STDC_HEADERS
17 #include <string.h>
18 #else /* STDC_HEADERS */
19 #ifndef HAVE_STRCHR
20 #define strchr index
21 #define strrchr index
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
24 #ifndef HAVE_MEMCPY
25 #define memcpy(d,s,n) bcopy ((s), (d), (n))
26 #define memmove(d,s,n) bcopy ((s), (d), (n))
27 #endif /* ! HAVE_MEMCPY */
28 #endif /* STDC_HEADERS */
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif /* HAVE_UNISTD_H */
33 #include <ctype.h>
34 #include <atalk/logger.h>
35 #include <atalk/util.h>
36
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
40
41 #include <atalk/dsi.h>
42 #include <atalk/atp.h>
43 #include <atalk/asp.h>
44 #include <atalk/nbp.h>
45 #include <atalk/afp.h>
46 #include <atalk/compat.h>
47 #include <atalk/server_child.h>
48 #ifdef USE_SRVLOC
49 #include <slp.h>
50 #endif /* USE_SRVLOC */
51 #ifdef HAVE_ACLS
52 #include <atalk/ldapconfig.h>
53 #endif
54
55 #include "globals.h"
56 #include "afp_config.h"
57 #include "uam_auth.h"
58 #include "status.h"
59 #include "volume.h"
60 #include "afp_zeroconf.h"
61
62 #define LINESIZE 1024  
63
64 /* get rid of unneeded configurations. i use reference counts to deal
65  * w/ multiple configs sharing the same afp_options. oh, to dream of
66  * garbage collection ... */
67 void configfree(AFPConfig *configs, const AFPConfig *config)
68 {
69     AFPConfig *p, *q;
70
71     for (p = configs; p; p = q) {
72         q = p->next;
73         if (p == config)
74             continue;
75
76         /* do a little reference counting */
77         if (--(*p->optcount) < 1) {
78             afp_options_free(&p->obj.options, p->defoptions);
79             free(p->optcount);
80         }
81
82         switch (p->obj.proto) {
83 #ifndef NO_DDP
84         case AFPPROTO_ASP:
85             free(p->obj.Obj);
86             free(p->obj.Type);
87             free(p->obj.Zone);
88             atp_close(((ASP) p->obj.handle)->asp_atp);
89             free(p->obj.handle);
90             break;
91 #endif /* no afp/asp */
92         case AFPPROTO_DSI:
93             close(p->fd);
94             free(p->obj.handle);
95             break;
96         }
97         free(p);
98     }
99
100     /* the master loaded the volumes for zeroconf, get rid of that */
101     unload_volumes_and_extmap();
102 }
103
104 #ifdef USE_SRVLOC
105 static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) {
106     *(SLPError*)cookie = errcode;
107 }
108
109 static char hex[17] = "0123456789abcdef";
110
111 static char * srvloc_encode(const struct afp_options *options, const char *name)
112 {
113         static char buf[512];
114         char *conv_name;
115         unsigned char *p;
116         unsigned int i = 0;
117 #ifndef NO_DDP
118         char *Obj, *Type = "", *Zone = "";
119 #endif
120
121         /* Convert name to maccharset */
122         if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
123                          name, -1, &conv_name)) )
124                 return (char*)name;
125
126         /* Escape characters */
127         p = conv_name;
128         while (*p && i<(sizeof(buf)-4)) {
129             if (*p == '@')
130                 break;
131             else if (isspace(*p)) {
132                 buf[i++] = '%';
133                 buf[i++] = '2';
134                 buf[i++] = '0';
135                 p++;
136             }   
137             else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
138                 buf[i++] = '%';
139                 buf[i++] = hex[*p >> 4];
140                 buf[i++] = hex[*p++ & 15];
141             }
142             else {
143                 buf[i++] = *p++;
144             }
145         }
146         buf[i] = '\0';
147
148 #ifndef NO_DDP
149         /* Add ZONE,  */
150         if (nbp_name(options->server, &Obj, &Type, &Zone )) {
151                 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
152         }
153         else {
154                 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
155         }
156 #endif
157         free (conv_name);
158
159         return buf;
160 }
161 #endif /* USE_SRVLOC */
162
163 static void dsi_cleanup(const AFPConfig *config)
164 {
165 #ifdef USE_SRVLOC
166     SLPError err;
167     SLPError callbackerr;
168     SLPHandle hslp;
169     DSI *dsi = (DSI *)config->obj.handle;
170
171     /*  Do nothing if we didn't register.  */
172     if (!dsi || dsi->srvloc_url[0] == '\0')
173         return;
174
175     err = SLPOpen("en", SLP_FALSE, &hslp);
176     if (err != SLP_OK) {
177         LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
178         goto srvloc_dereg_err;
179     }
180
181     err = SLPDereg(hslp,
182                    dsi->srvloc_url,
183                    SRVLOC_callback,
184                    &callbackerr);
185     if (err != SLP_OK) {
186         LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
187         goto srvloc_dereg_err;
188     }
189
190     if (callbackerr != SLP_OK) {
191         LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
192         goto srvloc_dereg_err;
193     }
194
195 srvloc_dereg_err:
196     dsi->srvloc_url[0] = '\0';
197     SLPClose(hslp);
198 #endif /* USE_SRVLOC */
199 }
200
201 #ifndef NO_DDP
202 static void asp_cleanup(const AFPConfig *config)
203 {
204     /* we need to stop tickle handler */
205     asp_stop_tickle();
206     nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
207                 &config->obj.options.ddpaddr);
208 }
209
210 /* these two are almost identical. it should be possible to collapse them
211  * into one with minimal junk. */
212 static int asp_start(AFPConfig *config, AFPConfig *configs,
213                      server_child *server_children)
214 {
215     ASP asp;
216
217     if (!(asp = asp_getsession(config->obj.handle, server_children,
218                                config->obj.options.tickleval))) {
219         LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
220         exit( EXITERR_CLNT );
221     }
222
223     if (asp->child) {
224         configfree(configs, config); /* free a bunch of stuff */
225         afp_over_asp(&config->obj);
226         exit (0);
227     }
228
229     return 0;
230 }
231 #endif /* no afp/asp */
232
233 static int dsi_start(AFPConfig *config, AFPConfig *configs,
234                      server_child *server_children)
235 {
236     DSI *dsi;
237
238     if (!(dsi = dsi_getsession(config->obj.handle, server_children,
239                                config->obj.options.tickleval))) {
240         LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
241         exit( EXITERR_CLNT );
242     }
243
244     /* we've forked. */
245     if (dsi->child) {
246         configfree(configs, config);
247         afp_over_dsi(&config->obj); /* start a session */
248         exit (0);
249     }
250
251     return 0;
252 }
253
254 #ifndef NO_DDP
255 static AFPConfig *ASPConfigInit(const struct afp_options *options,
256                                 unsigned char *refcount)
257 {
258     AFPConfig *config;
259     ATP atp;
260     ASP asp;
261     char *Obj, *Type = "AFPServer", *Zone = "*";
262     char *convname = NULL;
263
264     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
265         return NULL;
266
267     if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
268         LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
269         free(config);
270         return NULL;
271     }
272
273     if ((asp = asp_init( atp )) == NULL) {
274         LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
275         atp_close(atp);
276         free(config);
277         return NULL;
278     }
279
280     /* register asp server */
281     Obj = (char *) options->hostname;
282     if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
283                          options->server, strlen(options->server), &convname)) ) {
284         if ((convname = strdup(options->server)) == NULL ) {
285             LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
286             goto serv_free_return;
287         }
288     }
289
290     if (nbp_name(convname, &Obj, &Type, &Zone )) {
291         LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
292         goto serv_free_return;
293     }
294     if (convname)
295         free (convname);
296
297     /* dup Obj, Type and Zone as they get assigned to a single internal
298      * buffer by nbp_name */
299     if ((config->obj.Obj  = strdup(Obj)) == NULL)
300         goto serv_free_return;
301
302     if ((config->obj.Type = strdup(Type)) == NULL) {
303         free(config->obj.Obj);
304         goto serv_free_return;
305     }
306
307     if ((config->obj.Zone = strdup(Zone)) == NULL) {
308         free(config->obj.Obj);
309         free(config->obj.Type);
310         goto serv_free_return;
311     }
312
313     /* make sure we're not registered */
314     nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
315     if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
316         LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
317         free(config->obj.Obj);
318         free(config->obj.Type);
319         free(config->obj.Zone);
320         goto serv_free_return;
321     }
322
323     LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
324         ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
325         atp_sockaddr( atp )->sat_addr.s_node,
326         atp_sockaddr( atp )->sat_port, VERSION );
327
328     config->fd = atp_fileno(atp);
329     config->obj.handle = asp;
330     config->obj.config = config;
331     config->obj.proto = AFPPROTO_ASP;
332
333     memcpy(&config->obj.options, options, sizeof(struct afp_options));
334     config->optcount = refcount;
335     (*refcount)++;
336
337     config->server_start = asp_start;
338     config->server_cleanup = asp_cleanup;
339
340     return config;
341
342 serv_free_return:
343                     asp_close(asp);
344     free(config);
345     return NULL;
346 }
347 #endif /* no afp/asp */
348
349
350 static AFPConfig *DSIConfigInit(const struct afp_options *options,
351                                 unsigned char *refcount,
352                                 const dsi_proto protocol)
353 {
354     AFPConfig *config;
355     DSI *dsi;
356     char *p, *q;
357
358     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
359         LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
360         return NULL;
361     }
362
363     LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
364         options->hostname,
365         options->ipaddr ? options->ipaddr : "default",
366         options->port ? options->port : "548");
367
368     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
369                         options->ipaddr, options->port,
370                         options->flags & OPTION_PROXY,
371                         options->server_quantum)) == NULL) {
372         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
373         free(config);
374         return NULL;
375     }
376
377     if (options->flags & OPTION_PROXY) {
378         LOG(log_info, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
379             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
380     } else {
381         LOG(log_info, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
382             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
383     }
384
385 #ifdef USE_SRVLOC
386     dsi->srvloc_url[0] = '\0';  /*  Mark that we haven't registered.  */
387     if (!(options->flags & OPTION_NOSLP)) {
388         SLPError err;
389         SLPError callbackerr;
390         SLPHandle hslp;
391         unsigned int afp_port;
392         int   l;
393         char *srvloc_hostname;
394         const char *hostname;
395
396         err = SLPOpen("en", SLP_FALSE, &hslp);
397         if (err != SLP_OK) {
398             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
399             goto srvloc_reg_err;
400         }
401
402         /* XXX We don't want to tack on the port number if we don't have to.
403          * Why?
404          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
405          * use a non-default port, they can, but be aware, this server might
406          * not show up int the Network Browser.
407          */
408         afp_port = getip_port((struct sockaddr *)&dsi->server);
409         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
410         p = NULL;
411         if (options->fqdn) {
412             hostname = options->fqdn;
413             p = strchr(hostname, ':');
414         }       
415         else 
416             hostname = getip_string((struct sockaddr *)&dsi->server);
417
418         srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
419
420         if ((p) || afp_port == 548) {
421             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
422         }
423         else {
424             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
425         }
426
427         if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
428             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
429             dsi->srvloc_url[0] = '\0';
430             goto srvloc_reg_err;
431         }
432
433         err = SLPReg(hslp,
434                      dsi->srvloc_url,
435                      SLP_LIFETIME_MAXIMUM,
436                      "afp",
437                      "",
438                      SLP_TRUE,
439                      SRVLOC_callback,
440                      &callbackerr);
441         if (err != SLP_OK) {
442             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
443             dsi->srvloc_url[0] = '\0';
444             goto srvloc_reg_err;
445         }
446
447         if (callbackerr != SLP_OK) {
448             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
449             dsi->srvloc_url[0] = '\0';
450             goto srvloc_reg_err;
451         }
452
453         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
454         config->server_cleanup = dsi_cleanup;
455
456 srvloc_reg_err:
457         SLPClose(hslp);
458     }
459 #endif /* USE_SRVLOC */
460
461     config->fd = dsi->serversock;
462     config->obj.handle = dsi;
463     config->obj.config = config;
464     config->obj.proto = AFPPROTO_DSI;
465
466     memcpy(&config->obj.options, options, sizeof(struct afp_options));
467     /* get rid of any appletalk info. we use the fact that the DSI
468      * stuff is done after the ASP stuff. */
469     p = config->obj.options.server;
470     if (p && (q = strchr(p, ':')))
471         *q = '\0';
472
473     config->optcount = refcount;
474     (*refcount)++;
475
476     config->server_start = dsi_start;
477     return config;
478 }
479
480 /* allocate server configurations. this should really store the last
481  * entry in config->last or something like that. that would make
482  * supporting multiple dsi transports easier. */
483 static AFPConfig *AFPConfigInit(struct afp_options *options,
484                                 const struct afp_options *defoptions)
485 {
486     AFPConfig *config = NULL, *next = NULL;
487     unsigned char *refcount;
488
489     if ((refcount = (unsigned char *)
490                     calloc(1, sizeof(unsigned char))) == NULL) {
491         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
492         return NULL;
493     }
494
495 #ifndef NO_DDP
496     /* handle asp transports */
497     if ((options->transports & AFPTRANS_DDP) &&
498             (config = ASPConfigInit(options, refcount)))
499         config->defoptions = defoptions;
500 #endif /* NO_DDP */
501
502
503     /* set signature */
504     set_signature(options);
505
506     /* handle dsi transports and dsi proxies. we only proxy
507      * for DSI connections. */
508
509     /* this should have something like the following:
510      * for (i=mindsi; i < maxdsi; i++)
511      *   if (options->transports & (1 << i) && 
512      *     (next = DSIConfigInit(options, refcount, i)))
513      *     next->defoptions = defoptions;
514      */
515     if ((options->transports & AFPTRANS_TCP) &&
516             (((options->flags & OPTION_PROXY) == 0) ||
517              ((options->flags & OPTION_PROXY) && config))
518             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
519         next->defoptions = defoptions;
520
521     /* load in all the authentication modules. we can load the same
522        things multiple times if necessary. however, loading different
523        things with the same names will cause complaints. by not loading
524        in any uams with proxies, we prevent ddp connections from succeeding.
525     */
526     auth_load(options->uampath, options->uamlist);
527
528     /* this should be able to accept multiple dsi transports. i think
529      * the only thing that gets affected is the net addresses. */
530     status_init(config, next, options);
531
532     /* attach dsi config to tail of asp config */
533     if (config) {
534         config->next = next;
535         return config;
536     }
537
538     return next;
539 }
540
541 /* fill in the appropriate bits for each interface */
542 AFPConfig *configinit(struct afp_options *cmdline)
543 {
544     FILE *fp;
545     char buf[LINESIZE + 1], *p, have_option = 0;
546     size_t len;
547     struct afp_options options;
548     AFPConfig *config=NULL, *first = NULL; 
549
550 #ifdef HAVE_ACLS
551     /* Parse afp_ldap.conf first so we can set the uuid option */
552     acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
553 #endif /* HAVE_ACLS */
554
555     /* if config file doesn't exist, load defaults */
556     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
557     {
558         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
559             cmdline->configfile);
560         return AFPConfigInit(cmdline, cmdline);
561     }
562
563     /* scan in the configuration file */
564     len = 0;
565     while (!feof(fp)) {
566         if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
567             continue;
568         len = strlen(buf);
569         if ( len >= 2 && buf[len-2] == '\\' ) {
570             len -= 2;
571             continue;
572         } else
573             len = 0;
574
575         /* a little pre-processing to get rid of spaces and end-of-lines */
576         p = buf;
577         while (p && isspace(*p))
578             p++;
579         if (!p || (*p == '\0'))
580             continue;
581
582         have_option = 1;
583
584         memcpy(&options, cmdline, sizeof(options));
585         if (!afp_options_parseline(p, &options))
586             continue;
587
588 #ifdef HAVE_ACLS
589         /* Enable UUID support if LDAP config is complete */
590         if (ldap_config_valid) {
591             LOG(log_info, logtype_afpd, "Enabling UUID support");
592             options.flags |= OPTION_UUID;
593         }
594 #endif /* HAVE_ACLS */
595
596         /* AFPConfigInit can return two linked configs due to DSI and ASP */
597         if (!first) {
598             if ((first = AFPConfigInit(&options, cmdline)))
599                 config = first->next ? first->next : first;
600         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
601             config = config->next->next ? config->next->next : config->next;
602         }
603     }
604
605     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
606     fclose(fp);
607
608     if (!have_option)
609         first = AFPConfigInit(cmdline, cmdline);
610
611     /* Now register with zeroconf, we also need the volumes for that */
612     load_volumes(&first->obj);
613     zeroconf_register(first);
614
615     return first;
616 }