2 * $Id: afp_config.c,v 1.29 2009-11-05 14:38:07 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, -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)
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 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
370 options->ipaddr ? options->ipaddr : "default",
371 options->port ? options->port : "548");
373 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
374 options->ipaddr, options->port,
375 options->flags & OPTION_PROXY,
376 options->server_quantum)) == NULL) {
377 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
382 if (options->flags & OPTION_PROXY) {
383 LOG(log_info, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
384 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
386 LOG(log_info, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
387 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
391 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
392 if (!(options->flags & OPTION_NOSLP)) {
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 afpovertcp = getservbyname("afpovertcp", "tcp");
406 if (afpovertcp != NULL) {
407 afp_port = afpovertcp->s_port;
409 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
412 hostname = options->fqdn;
413 p = strchr(hostname, ':');
416 hostname = inet_ntoa(dsi->server.sin_addr);
417 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
419 if (strlen(srvloc_hostname) > (sizeof(dsi->srvloc_url) - strlen(hostname) - 21)) {
420 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
421 dsi->srvloc_url[0] = '\0';
424 if ((p) || dsi->server.sin_port == afp_port) {
425 sprintf(dsi->srvloc_url, "afp://%s/?NAME=%s", hostname, srvloc_hostname);
428 sprintf(dsi->srvloc_url, "afp://%s:%d/?NAME=%s", hostname, ntohs(dsi->server.sin_port), srvloc_hostname);
433 SLP_LIFETIME_MAXIMUM,
440 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
441 dsi->srvloc_url[0] = '\0';
445 if (callbackerr != SLP_OK) {
446 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
447 dsi->srvloc_url[0] = '\0';
451 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
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;
476 config->server_cleanup = dsi_cleanup;
481 /* allocate server configurations. this should really store the last
482 * entry in config->last or something like that. that would make
483 * supporting multiple dsi transports easier. */
484 static AFPConfig *AFPConfigInit(const struct afp_options *options,
485 const struct afp_options *defoptions)
487 AFPConfig *config = NULL, *next = NULL;
488 unsigned char *refcount;
490 if ((refcount = (unsigned char *)
491 calloc(1, sizeof(unsigned char))) == NULL) {
492 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
497 /* handle asp transports */
498 if ((options->transports & AFPTRANS_DDP) &&
499 (config = ASPConfigInit(options, refcount)))
500 config->defoptions = defoptions;
503 /* handle dsi transports and dsi proxies. we only proxy
504 * for DSI connections. */
506 /* this should have something like the following:
507 * for (i=mindsi; i < maxdsi; i++)
508 * if (options->transports & (1 << i) &&
509 * (next = DSIConfigInit(options, refcount, i)))
510 * next->defoptions = defoptions;
512 if ((options->transports & AFPTRANS_TCP) &&
513 (((options->flags & OPTION_PROXY) == 0) ||
514 ((options->flags & OPTION_PROXY) && config))
515 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
516 next->defoptions = defoptions;
518 /* load in all the authentication modules. we can load the same
519 things multiple times if necessary. however, loading different
520 things with the same names will cause complaints. by not loading
521 in any uams with proxies, we prevent ddp connections from succeeding.
523 auth_load(options->uampath, options->uamlist);
525 /* this should be able to accept multiple dsi transports. i think
526 * the only thing that gets affected is the net addresses. */
527 status_init(config, next, options);
529 /* attach dsi config to tail of asp config */
538 /* fill in the appropriate bits for each interface */
539 AFPConfig *configinit(struct afp_options *cmdline)
542 char buf[LINESIZE + 1], *p, have_option = 0;
544 struct afp_options options;
545 AFPConfig *config=NULL, *first = NULL;
547 #ifdef HAVE_NFSv4_ACLS
548 /* Parse ldap.conf first so we can set the uuid option */
549 LOG(log_debug, logtype_afpd, "Start parsing ldap.conf");
550 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
551 LOG(log_debug, logtype_afpd, "Finished parsing ldap.conf");
555 /* if config file doesn't exist, load defaults */
556 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
558 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
559 cmdline->configfile);
560 return AFPConfigInit(cmdline, cmdline);
563 LOG(log_debug, logtype_afpd, "Loading ConfigFile");
565 /* scan in the configuration file */
568 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
571 if ( len >= 2 && buf[len-2] == '\\' ) {
577 /* a little pre-processing to get rid of spaces and end-of-lines */
579 while (p && isspace(*p))
581 if (!p || (*p == '\0'))
586 memcpy(&options, cmdline, sizeof(options));
587 if (!afp_options_parseline(p, &options))
590 #ifdef HAVE_NFSv4_ACLS
591 /* Enable UUID support if LDAP config is complete */
592 if (ldap_config_valid)
593 options.flags |= OPTION_UUID;
596 /* this should really get a head and a tail to simplify things. */
598 if ((first = AFPConfigInit(&options, cmdline)))
599 config = first->next ? first->next : first;
600 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
601 config = config->next->next ? config->next->next : config->next;
605 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
609 first = AFPConfigInit(cmdline, cmdline);