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