2 * $Id: afp_config.c,v 1.26 2009-02-02 11:55:00 franklahm Exp $
4 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
20 #else /* STDC_HEADERS */
24 #endif /* HAVE_STRCHR */
25 char *strchr (), *strrchr ();
27 #define memcpy(d,s,n) bcopy ((s), (d), (n))
28 #define memmove(d,s,n) bcopy ((s), (d), (n))
29 #endif /* ! HAVE_MEMCPY */
30 #endif /* STDC_HEADERS */
34 #endif /* HAVE_UNISTD_H */
36 #include <atalk/logger.h>
37 #include <atalk/util.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
43 #include <atalk/dsi.h>
44 #include <atalk/atp.h>
45 #include <atalk/asp.h>
46 #include <atalk/nbp.h>
47 #include <atalk/afp.h>
48 #include <atalk/compat.h>
49 #include <atalk/server_child.h>
52 #endif /* USE_SRVLOC */
53 #ifdef HAVE_NFSv4_ACLS
54 #include <atalk/ldapconfig.h>
58 #include "afp_config.h"
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)
71 for (p = configs; p; p = q) {
76 /* do a little reference counting */
77 if (--(*p->optcount) < 1) {
78 afp_options_free(&p->obj.options, p->defoptions);
82 switch (p->obj.proto) {
88 atp_close(((ASP) p->obj.handle)->asp_atp);
91 #endif /* no afp/asp */
102 static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) {
103 *(SLPError*)cookie = errcode;
106 static char hex[17] = "0123456789abcdef";
108 static char * srvloc_encode(const struct afp_options *options, const char *name)
110 static char buf[512];
115 char *Obj, *Type = "", *Zone = "";
118 /* Convert name to maccharset */
119 if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
120 name, strlen(name), &conv_name)) )
123 /* Escape characters */
125 while (*p && i<(sizeof(buf)-4)) {
128 else if (isspace(*p)) {
134 else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
136 buf[i++] = hex[*p >> 4];
137 buf[i++] = hex[*p++ & 15];
147 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
148 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
151 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
158 #endif /* USE_SRVLOC */
161 static void dsi_cleanup(const AFPConfig *config)
164 SLPError callbackerr;
166 DSI *dsi = (DSI *)config->obj.handle;
168 /* Do nothing if we didn't register. */
169 if (!dsi || dsi->srvloc_url[0] == '\0')
172 err = SLPOpen("en", SLP_FALSE, &hslp);
174 LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
175 goto srvloc_dereg_err;
183 LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
184 goto srvloc_dereg_err;
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;
193 dsi->srvloc_url[0] = '\0';
196 #endif /* USE_SRVLOC */
199 static void asp_cleanup(const AFPConfig *config)
201 /* we need to stop tickle handler */
203 nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
204 &config->obj.options.ddpaddr);
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)
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 );
221 configfree(configs, config); /* free a bunch of stuff */
222 afp_over_asp(&config->obj);
228 #endif /* no afp/asp */
230 static int dsi_start(AFPConfig *config, AFPConfig *configs,
231 server_child *server_children)
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 );
243 configfree(configs, config);
244 afp_over_dsi(&config->obj); /* start a session */
252 static AFPConfig *ASPConfigInit(const struct afp_options *options,
253 unsigned char *refcount)
258 char *Obj, *Type = "AFPServer", *Zone = "*";
259 char *convname = NULL;
261 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
264 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
265 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
270 if ((asp = asp_init( atp )) == NULL) {
271 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
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;
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;
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;
299 if ((config->obj.Type = strdup(Type)) == NULL) {
300 free(config->obj.Obj);
301 goto serv_free_return;
304 if ((config->obj.Zone = strdup(Zone)) == NULL) {
305 free(config->obj.Obj);
306 free(config->obj.Type);
307 goto serv_free_return;
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;
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 );
325 config->fd = atp_fileno(atp);
326 config->obj.handle = asp;
327 config->obj.config = config;
328 config->obj.proto = AFPPROTO_ASP;
330 memcpy(&config->obj.options, options, sizeof(struct afp_options));
331 config->optcount = refcount;
334 config->server_start = asp_start;
335 config->server_cleanup = asp_cleanup;
344 #endif /* no afp/asp */
347 static AFPConfig *DSIConfigInit(const struct afp_options *options,
348 unsigned char *refcount,
349 const dsi_proto protocol)
356 SLPError callbackerr;
358 struct servent *afpovertcp;
360 char *srvloc_hostname, *hostname;
361 #endif /* USE_SRVLOC */
363 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
364 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
368 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
369 options->ipaddr, options->port,
370 options->flags & OPTION_PROXY,
371 options->server_quantum)) == NULL) {
372 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
377 if (options->flags & OPTION_PROXY) {
378 LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
379 inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
382 LOG(log_info, logtype_afpd, "ASIP started on %s:%d(%d) (%s)",
383 inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
384 dsi->serversock, VERSION);
388 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
389 if (!(options->flags & OPTION_NOSLP)) {
390 err = SLPOpen("en", SLP_FALSE, &hslp);
392 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
396 /* XXX We don't want to tack on the port number if we don't have to.
398 * Well, this seems to break MacOS < 10. If the user _really_ wants to
399 * use a non-default port, they can, but be aware, this server might
400 * not show up int the Network Browser.
402 afpovertcp = getservbyname("afpovertcp", "tcp");
403 if (afpovertcp != NULL) {
404 afp_port = afpovertcp->s_port;
406 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
409 hostname = options->fqdn;
410 p = strchr(hostname, ':');
413 hostname = inet_ntoa(dsi->server.sin_addr);
414 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
416 if (strlen(srvloc_hostname) > (sizeof(dsi->srvloc_url) - strlen(hostname) - 21)) {
417 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
418 dsi->srvloc_url[0] = '\0';
421 if ((p) || dsi->server.sin_port == afp_port) {
422 sprintf(dsi->srvloc_url, "afp://%s/?NAME=%s", hostname, srvloc_hostname);
425 sprintf(dsi->srvloc_url, "afp://%s:%d/?NAME=%s", hostname, ntohs(dsi->server.sin_port), srvloc_hostname);
430 SLP_LIFETIME_MAXIMUM,
437 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
438 dsi->srvloc_url[0] = '\0';
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';
448 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
453 #endif /* USE_SRVLOC */
456 config->fd = dsi->serversock;
457 config->obj.handle = dsi;
458 config->obj.config = config;
459 config->obj.proto = AFPPROTO_DSI;
461 memcpy(&config->obj.options, options, sizeof(struct afp_options));
462 /* get rid of any appletalk info. we use the fact that the DSI
463 * stuff is done after the ASP stuff. */
464 p = config->obj.options.server;
465 if (p && (q = strchr(p, ':')))
468 config->optcount = refcount;
471 config->server_start = dsi_start;
473 config->server_cleanup = dsi_cleanup;
478 /* allocate server configurations. this should really store the last
479 * entry in config->last or something like that. that would make
480 * supporting multiple dsi transports easier. */
481 static AFPConfig *AFPConfigInit(const struct afp_options *options,
482 const struct afp_options *defoptions)
484 AFPConfig *config = NULL, *next = NULL;
485 unsigned char *refcount;
487 if ((refcount = (unsigned char *)
488 calloc(1, sizeof(unsigned char))) == NULL) {
489 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
494 /* handle asp transports */
495 if ((options->transports & AFPTRANS_DDP) &&
496 (config = ASPConfigInit(options, refcount)))
497 config->defoptions = defoptions;
500 /* handle dsi transports and dsi proxies. we only proxy
501 * for DSI connections. */
503 /* this should have something like the following:
504 * for (i=mindsi; i < maxdsi; i++)
505 * if (options->transports & (1 << i) &&
506 * (next = DSIConfigInit(options, refcount, i)))
507 * next->defoptions = defoptions;
509 if ((options->transports & AFPTRANS_TCP) &&
510 (((options->flags & OPTION_PROXY) == 0) ||
511 ((options->flags & OPTION_PROXY) && config))
512 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
513 next->defoptions = defoptions;
515 /* load in all the authentication modules. we can load the same
516 things multiple times if necessary. however, loading different
517 things with the same names will cause complaints. by not loading
518 in any uams with proxies, we prevent ddp connections from succeeding.
520 auth_load(options->uampath, options->uamlist);
522 /* this should be able to accept multiple dsi transports. i think
523 * the only thing that gets affected is the net addresses. */
524 status_init(config, next, options);
526 /* attach dsi config to tail of asp config */
535 /* fill in the appropriate bits for each interface */
536 AFPConfig *configinit(struct afp_options *cmdline)
539 char buf[LINESIZE + 1], *p, have_option = 0;
541 struct afp_options options;
542 AFPConfig *config=NULL, *first = NULL;
545 /* if config file doesn't exist, load defaults */
546 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
548 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
549 cmdline->configfile);
550 return AFPConfigInit(cmdline, cmdline);
553 LOG(log_debug, logtype_afpd, "Loading ConfigFile");
555 /* scan in the configuration file */
558 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
561 if ( len >= 2 && buf[len-2] == '\\' ) {
567 /* a little pre-processing to get rid of spaces and end-of-lines */
569 while (p && isspace(*p))
571 if (!p || (*p == '\0'))
576 memcpy(&options, cmdline, sizeof(options));
577 if (!afp_options_parseline(p, &options))
580 /* this should really get a head and a tail to simplify things. */
582 if ((first = AFPConfigInit(&options, cmdline)))
583 config = first->next ? first->next : first;
584 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
585 config = config->next->next ? config->next->next : config->next;
589 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
593 first = AFPConfigInit(cmdline, cmdline);
595 #ifdef HAVE_NFSv4_ACLS
596 /* Parse ldap.conf */
597 LOG(log_debug, logtype_afpd, "Start parsing ldap.conf");
598 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
599 LOG(log_debug, logtype_afpd, "Finished parsing ldap.conf");
601 /* If we have a good LDAP config, enable UUID option for all apfd's */
604 if (ldap_config_valid)
605 config->obj.options.flags |= OPTION_UUID;
606 config = config->next;