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