2 * $Id: afp_config.c,v 1.30 2009-11-06 03:50:32 didg 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, -1, &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)
355 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
356 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
360 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
362 options->ipaddr ? options->ipaddr : "default",
363 options->port ? options->port : "548");
365 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
366 options->ipaddr, options->port,
367 options->flags & OPTION_PROXY,
368 options->server_quantum)) == NULL) {
369 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
374 if (options->flags & OPTION_PROXY) {
375 LOG(log_info, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
376 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
378 LOG(log_info, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
379 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
383 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
384 if (!(options->flags & OPTION_NOSLP)) {
386 SLPError callbackerr;
388 unsigned int afp_port;
390 char *srvloc_hostname;
391 const char *hostname;
393 err = SLPOpen("en", SLP_FALSE, &hslp);
395 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
399 /* XXX We don't want to tack on the port number if we don't have to.
401 * Well, this seems to break MacOS < 10. If the user _really_ wants to
402 * use a non-default port, they can, but be aware, this server might
403 * not show up int the Network Browser.
405 afp_port = getip_port((struct sockaddr *)&dsi->server);
406 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
409 hostname = options->fqdn;
410 p = strchr(hostname, ':');
413 hostname = getip_string((struct sockaddr *)&dsi->server);
415 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
417 if ((p) || afp_port == 548) {
418 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
421 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
424 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
425 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
426 dsi->srvloc_url[0] = '\0';
432 SLP_LIFETIME_MAXIMUM,
439 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
440 dsi->srvloc_url[0] = '\0';
444 if (callbackerr != SLP_OK) {
445 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
446 dsi->srvloc_url[0] = '\0';
450 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
451 config->server_cleanup = dsi_cleanup;
456 #endif /* USE_SRVLOC */
459 config->fd = dsi->serversock;
460 config->obj.handle = dsi;
461 config->obj.config = config;
462 config->obj.proto = AFPPROTO_DSI;
464 memcpy(&config->obj.options, options, sizeof(struct afp_options));
465 /* get rid of any appletalk info. we use the fact that the DSI
466 * stuff is done after the ASP stuff. */
467 p = config->obj.options.server;
468 if (p && (q = strchr(p, ':')))
471 config->optcount = refcount;
474 config->server_start = dsi_start;
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;
544 #ifdef HAVE_NFSv4_ACLS
545 /* Parse ldap.conf first so we can set the uuid option */
546 LOG(log_debug, logtype_afpd, "Start parsing ldap.conf");
547 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
548 LOG(log_debug, logtype_afpd, "Finished parsing ldap.conf");
552 /* if config file doesn't exist, load defaults */
553 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
555 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
556 cmdline->configfile);
557 return AFPConfigInit(cmdline, cmdline);
560 LOG(log_debug, logtype_afpd, "Loading ConfigFile");
562 /* scan in the configuration file */
565 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
568 if ( len >= 2 && buf[len-2] == '\\' ) {
574 /* a little pre-processing to get rid of spaces and end-of-lines */
576 while (p && isspace(*p))
578 if (!p || (*p == '\0'))
583 memcpy(&options, cmdline, sizeof(options));
584 if (!afp_options_parseline(p, &options))
587 #ifdef HAVE_NFSv4_ACLS
588 /* Enable UUID support if LDAP config is complete */
589 if (ldap_config_valid)
590 options.flags |= OPTION_UUID;
593 /* this should really get a head and a tail to simplify things. */
595 if ((first = AFPConfigInit(&options, cmdline)))
596 config = first->next ? first->next : first;
597 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
598 config = config->next->next ? config->next->next : config->next;
602 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
606 first = AFPConfigInit(cmdline, cmdline);