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