]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/afp_config.c
Fix regression introduced by previous IPC fds fix
[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 <atalk/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         /* we've forked. */
227         configfree(configs, config);
228         afp_over_dsi(&config->obj); /* start a session */
229         exit (0);
230     }
231
232     return child;
233 }
234
235 #ifndef NO_DDP
236 static AFPConfig *ASPConfigInit(const struct afp_options *options,
237                                 unsigned char *refcount)
238 {
239     AFPConfig *config;
240     ATP atp;
241     ASP asp;
242     char *Obj, *Type = "AFPServer", *Zone = "*";
243     char *convname = NULL;
244
245     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
246         return NULL;
247
248     if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL)  {
249         LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
250         free(config);
251         return NULL;
252     }
253
254     if ((asp = asp_init( atp )) == NULL) {
255         LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
256         atp_close(atp);
257         free(config);
258         return NULL;
259     }
260
261     /* register asp server */
262     Obj = (char *) options->hostname;
263     if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
264                          options->server, strlen(options->server), &convname)) ) {
265         if ((convname = strdup(options->server)) == NULL ) {
266             LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
267             goto serv_free_return;
268         }
269     }
270
271     if (nbp_name(convname, &Obj, &Type, &Zone )) {
272         LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
273         goto serv_free_return;
274     }
275     if (convname)
276         free (convname);
277
278     /* dup Obj, Type and Zone as they get assigned to a single internal
279      * buffer by nbp_name */
280     if ((config->obj.Obj  = strdup(Obj)) == NULL)
281         goto serv_free_return;
282
283     if ((config->obj.Type = strdup(Type)) == NULL) {
284         free(config->obj.Obj);
285         goto serv_free_return;
286     }
287
288     if ((config->obj.Zone = strdup(Zone)) == NULL) {
289         free(config->obj.Obj);
290         free(config->obj.Type);
291         goto serv_free_return;
292     }
293
294     /* make sure we're not registered */
295     nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
296     if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
297         LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
298         free(config->obj.Obj);
299         free(config->obj.Type);
300         free(config->obj.Zone);
301         goto serv_free_return;
302     }
303
304     LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
305         ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
306         atp_sockaddr( atp )->sat_addr.s_node,
307         atp_sockaddr( atp )->sat_port, VERSION );
308
309     config->fd = atp_fileno(atp);
310     config->obj.handle = asp;
311     config->obj.config = config;
312     config->obj.proto = AFPPROTO_ASP;
313
314     memcpy(&config->obj.options, options, sizeof(struct afp_options));
315     config->optcount = refcount;
316     (*refcount)++;
317
318     config->server_start = asp_start;
319     config->server_cleanup = asp_cleanup;
320
321     return config;
322
323 serv_free_return:
324                     asp_close(asp);
325     free(config);
326     return NULL;
327 }
328 #endif /* no afp/asp */
329
330
331 static AFPConfig *DSIConfigInit(const struct afp_options *options,
332                                 unsigned char *refcount,
333                                 const dsi_proto protocol)
334 {
335     AFPConfig *config;
336     DSI *dsi;
337     char *p, *q;
338
339     if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
340         LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
341         return NULL;
342     }
343
344     LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
345         options->hostname,
346         options->ipaddr ? options->ipaddr : "default",
347         options->port ? options->port : "548");
348
349     if ((dsi = dsi_init(protocol, "afpd", options->hostname,
350                         options->ipaddr, options->port,
351                         options->flags & OPTION_PROXY,
352                         options->server_quantum)) == NULL) {
353         LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
354         free(config);
355         return NULL;
356     }
357     dsi->dsireadbuf = options->dsireadbuf;
358
359     if (options->flags & OPTION_PROXY) {
360         LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
361             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
362     } else {
363         LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
364             getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
365     }
366
367 #ifdef USE_SRVLOC
368     dsi->srvloc_url[0] = '\0';  /*  Mark that we haven't registered.  */
369     if (!(options->flags & OPTION_NOSLP)) {
370         SLPError err;
371         SLPError callbackerr;
372         SLPHandle hslp;
373         unsigned int afp_port;
374         int   l;
375         char *srvloc_hostname;
376         const char *hostname;
377
378         err = SLPOpen("en", SLP_FALSE, &hslp);
379         if (err != SLP_OK) {
380             LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
381             goto srvloc_reg_err;
382         }
383
384         /* XXX We don't want to tack on the port number if we don't have to.
385          * Why?
386          * Well, this seems to break MacOS < 10.  If the user _really_ wants to
387          * use a non-default port, they can, but be aware, this server might
388          * not show up int the Network Browser.
389          */
390         afp_port = getip_port((struct sockaddr *)&dsi->server);
391         /* If specified use the FQDN to register with srvloc, otherwise use IP. */
392         p = NULL;
393         if (options->fqdn) {
394             hostname = options->fqdn;
395             p = strchr(hostname, ':');
396         }       
397         else 
398             hostname = getip_string((struct sockaddr *)&dsi->server);
399
400         srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
401
402         if ((p) || afp_port == 548) {
403             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
404         }
405         else {
406             l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
407         }
408
409         if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
410             LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
411             dsi->srvloc_url[0] = '\0';
412             goto srvloc_reg_err;
413         }
414
415         err = SLPReg(hslp,
416                      dsi->srvloc_url,
417                      SLP_LIFETIME_MAXIMUM,
418                      "afp",
419                      "",
420                      SLP_TRUE,
421                      SRVLOC_callback,
422                      &callbackerr);
423         if (err != SLP_OK) {
424             LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
425             dsi->srvloc_url[0] = '\0';
426             goto srvloc_reg_err;
427         }
428
429         if (callbackerr != SLP_OK) {
430             LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
431             dsi->srvloc_url[0] = '\0';
432             goto srvloc_reg_err;
433         }
434
435         LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
436         config->server_cleanup = dsi_cleanup;
437
438 srvloc_reg_err:
439         SLPClose(hslp);
440     }
441 #endif /* USE_SRVLOC */
442
443     config->fd = dsi->serversock;
444     config->obj.handle = dsi;
445     dsi->AFPobj = &config->obj;
446     config->obj.config = config;
447     config->obj.proto = AFPPROTO_DSI;
448
449     memcpy(&config->obj.options, options, sizeof(struct afp_options));
450     /* get rid of any appletalk info. we use the fact that the DSI
451      * stuff is done after the ASP stuff. */
452     p = config->obj.options.server;
453     if (p && (q = strchr(p, ':')))
454         *q = '\0';
455
456     config->optcount = refcount;
457     (*refcount)++;
458
459     config->server_start = dsi_start;
460     return config;
461 }
462
463 /* allocate server configurations. this should really store the last
464  * entry in config->last or something like that. that would make
465  * supporting multiple dsi transports easier. */
466 static AFPConfig *AFPConfigInit(struct afp_options *options,
467                                 const struct afp_options *defoptions)
468 {
469     AFPConfig *config = NULL, *next = NULL;
470     unsigned char *refcount;
471
472     if ((refcount = (unsigned char *)
473                     calloc(1, sizeof(unsigned char))) == NULL) {
474         LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
475         return NULL;
476     }
477
478 #ifndef NO_DDP
479     /* handle asp transports */
480     if ((options->transports & AFPTRANS_DDP) &&
481             (config = ASPConfigInit(options, refcount)))
482         config->defoptions = defoptions;
483 #endif /* NO_DDP */
484
485
486     /* set signature */
487     set_signature(options);
488
489     /* handle dsi transports and dsi proxies. we only proxy
490      * for DSI connections. */
491
492     /* this should have something like the following:
493      * for (i=mindsi; i < maxdsi; i++)
494      *   if (options->transports & (1 << i) && 
495      *     (next = DSIConfigInit(options, refcount, i)))
496      *     next->defoptions = defoptions;
497      */
498     if ((options->transports & AFPTRANS_TCP) &&
499             (((options->flags & OPTION_PROXY) == 0) ||
500              ((options->flags & OPTION_PROXY) && config))
501             && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
502         next->defoptions = defoptions;
503
504     /* load in all the authentication modules. we can load the same
505        things multiple times if necessary. however, loading different
506        things with the same names will cause complaints. by not loading
507        in any uams with proxies, we prevent ddp connections from succeeding.
508     */
509     auth_load(options->uampath, options->uamlist);
510
511     /* this should be able to accept multiple dsi transports. i think
512      * the only thing that gets affected is the net addresses. */
513     status_init(config, next, options);
514
515     /* attach dsi config to tail of asp config */
516     if (config) {
517         config->next = next;
518         return config;
519     }
520
521     return next;
522 }
523
524 /* fill in the appropriate bits for each interface */
525 AFPConfig *configinit(struct afp_options *cmdline)
526 {
527     FILE *fp;
528     char buf[LINESIZE + 1], *p, have_option = 0;
529     size_t len;
530     struct afp_options options;
531     AFPConfig *config=NULL, *first = NULL; 
532
533     /* if config file doesn't exist, load defaults */
534     if ((fp = fopen(cmdline->configfile, "r")) == NULL)
535     {
536         LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
537             cmdline->configfile);
538         return AFPConfigInit(cmdline, cmdline);
539     }
540
541     /* scan in the configuration file */
542     len = 0;
543     while (!feof(fp)) {
544         if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
545             continue;
546         len = strlen(buf);
547         if ( len >= 2 && buf[len-2] == '\\' ) {
548             len -= 2;
549             continue;
550         } else
551             len = 0;
552
553         /* a little pre-processing to get rid of spaces and end-of-lines */
554         p = buf;
555         while (p && isspace(*p))
556             p++;
557         if (!p || (*p == '\0'))
558             continue;
559
560         have_option = 1;
561
562         memcpy(&options, cmdline, sizeof(options));
563         if (!afp_options_parseline(p, &options))
564             continue;
565
566         /* AFPConfigInit can return two linked configs due to DSI and ASP */
567         if (!first) {
568             if ((first = AFPConfigInit(&options, cmdline)))
569                 config = first->next ? first->next : first;
570         } else if ((config->next = AFPConfigInit(&options, cmdline))) {
571             config = config->next->next ? config->next->next : config->next;
572         }
573     }
574
575 #ifdef HAVE_LDAP
576     /* Parse afp_ldap.conf */
577     acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
578 #endif /* HAVE_LDAP */
579
580     LOG(log_debug, logtype_afpd, "Finished parsing Config File");
581     fclose(fp);
582
583     if (!have_option)
584         first = AFPConfigInit(cmdline, cmdline);
585
586     /* Now register with zeroconf, we also need the volumes for that */
587     if (first && !(first->obj.options.flags & OPTION_NOZEROCONF)) {
588         load_volumes(&first->obj);
589         zeroconf_register(first);
590     }
591
592     return first;
593 }