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