2 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
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 */
32 #endif /* HAVE_UNISTD_H */
34 #include <atalk/logger.h>
35 #include <atalk/util.h>
37 #include <sys/socket.h>
38 #include <netinet/in.h>
39 #include <arpa/inet.h>
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>
50 #endif /* USE_SRVLOC */
52 #include <atalk/ldapconfig.h>
56 #include "afp_config.h"
60 #include "afp_zeroconf.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 */
100 /* the master loaded the volumes for zeroconf, get rid of that */
101 unload_volumes_and_extmap();
105 static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) {
106 *(SLPError*)cookie = errcode;
109 static char hex[17] = "0123456789abcdef";
111 static char * srvloc_encode(const struct afp_options *options, const char *name)
113 static char buf[512];
118 char *Obj, *Type = "", *Zone = "";
121 /* Convert name to maccharset */
122 if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
123 name, -1, &conv_name)) )
126 /* Escape characters */
128 while (*p && i<(sizeof(buf)-4)) {
131 else if (isspace(*p)) {
137 else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
139 buf[i++] = hex[*p >> 4];
140 buf[i++] = hex[*p++ & 15];
150 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
151 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
154 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
161 #endif /* USE_SRVLOC */
163 static void dsi_cleanup(const AFPConfig *config)
167 SLPError callbackerr;
169 DSI *dsi = (DSI *)config->obj.handle;
171 /* Do nothing if we didn't register. */
172 if (!dsi || dsi->srvloc_url[0] == '\0')
175 err = SLPOpen("en", SLP_FALSE, &hslp);
177 LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
178 goto srvloc_dereg_err;
186 LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
187 goto srvloc_dereg_err;
190 if (callbackerr != SLP_OK) {
191 LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
192 goto srvloc_dereg_err;
196 dsi->srvloc_url[0] = '\0';
198 #endif /* USE_SRVLOC */
202 static void asp_cleanup(const AFPConfig *config)
204 /* we need to stop tickle handler */
206 nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
207 &config->obj.options.ddpaddr);
210 /* these two are almost identical. it should be possible to collapse them
211 * into one with minimal junk. */
212 static int asp_start(AFPConfig *config, AFPConfig *configs,
213 server_child *server_children)
217 if (!(asp = asp_getsession(config->obj.handle, server_children,
218 config->obj.options.tickleval))) {
219 LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
220 exit( EXITERR_CLNT );
224 configfree(configs, config); /* free a bunch of stuff */
225 afp_over_asp(&config->obj);
231 #endif /* no afp/asp */
233 static int dsi_start(AFPConfig *config, AFPConfig *configs,
234 server_child *server_children)
238 if (!(dsi = dsi_getsession(config->obj.handle, server_children,
239 config->obj.options.tickleval))) {
240 LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
241 exit( EXITERR_CLNT );
246 configfree(configs, config);
247 afp_over_dsi(&config->obj); /* start a session */
255 static AFPConfig *ASPConfigInit(const struct afp_options *options,
256 unsigned char *refcount)
261 char *Obj, *Type = "AFPServer", *Zone = "*";
262 char *convname = NULL;
264 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
267 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
268 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
273 if ((asp = asp_init( atp )) == NULL) {
274 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
280 /* register asp server */
281 Obj = (char *) options->hostname;
282 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
283 options->server, strlen(options->server), &convname)) ) {
284 if ((convname = strdup(options->server)) == NULL ) {
285 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
286 goto serv_free_return;
290 if (nbp_name(convname, &Obj, &Type, &Zone )) {
291 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
292 goto serv_free_return;
297 /* dup Obj, Type and Zone as they get assigned to a single internal
298 * buffer by nbp_name */
299 if ((config->obj.Obj = strdup(Obj)) == NULL)
300 goto serv_free_return;
302 if ((config->obj.Type = strdup(Type)) == NULL) {
303 free(config->obj.Obj);
304 goto serv_free_return;
307 if ((config->obj.Zone = strdup(Zone)) == NULL) {
308 free(config->obj.Obj);
309 free(config->obj.Type);
310 goto serv_free_return;
313 /* make sure we're not registered */
314 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
315 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
316 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
317 free(config->obj.Obj);
318 free(config->obj.Type);
319 free(config->obj.Zone);
320 goto serv_free_return;
323 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
324 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
325 atp_sockaddr( atp )->sat_addr.s_node,
326 atp_sockaddr( atp )->sat_port, VERSION );
328 config->fd = atp_fileno(atp);
329 config->obj.handle = asp;
330 config->obj.config = config;
331 config->obj.proto = AFPPROTO_ASP;
333 memcpy(&config->obj.options, options, sizeof(struct afp_options));
334 config->optcount = refcount;
337 config->server_start = asp_start;
338 config->server_cleanup = asp_cleanup;
347 #endif /* no afp/asp */
350 static AFPConfig *DSIConfigInit(const struct afp_options *options,
351 unsigned char *refcount,
352 const dsi_proto protocol)
358 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
359 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
363 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
365 options->ipaddr ? options->ipaddr : "default",
366 options->port ? options->port : "548");
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, "AFP/TCP proxy initialized for %s:%d (%s)",
379 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
381 LOG(log_info, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
382 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
386 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
387 if (!(options->flags & OPTION_NOSLP)) {
389 SLPError callbackerr;
391 unsigned int afp_port;
393 char *srvloc_hostname;
394 const char *hostname;
396 err = SLPOpen("en", SLP_FALSE, &hslp);
398 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
402 /* XXX We don't want to tack on the port number if we don't have to.
404 * Well, this seems to break MacOS < 10. If the user _really_ wants to
405 * use a non-default port, they can, but be aware, this server might
406 * not show up int the Network Browser.
408 afp_port = getip_port((struct sockaddr *)&dsi->server);
409 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
412 hostname = options->fqdn;
413 p = strchr(hostname, ':');
416 hostname = getip_string((struct sockaddr *)&dsi->server);
418 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
420 if ((p) || afp_port == 548) {
421 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
424 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
427 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
428 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
429 dsi->srvloc_url[0] = '\0';
435 SLP_LIFETIME_MAXIMUM,
442 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
443 dsi->srvloc_url[0] = '\0';
447 if (callbackerr != SLP_OK) {
448 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
449 dsi->srvloc_url[0] = '\0';
453 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
454 config->server_cleanup = dsi_cleanup;
459 #endif /* USE_SRVLOC */
461 config->fd = dsi->serversock;
462 config->obj.handle = dsi;
463 config->obj.config = config;
464 config->obj.proto = AFPPROTO_DSI;
466 memcpy(&config->obj.options, options, sizeof(struct afp_options));
467 /* get rid of any appletalk info. we use the fact that the DSI
468 * stuff is done after the ASP stuff. */
469 p = config->obj.options.server;
470 if (p && (q = strchr(p, ':')))
473 config->optcount = refcount;
476 config->server_start = dsi_start;
480 /* allocate server configurations. this should really store the last
481 * entry in config->last or something like that. that would make
482 * supporting multiple dsi transports easier. */
483 static AFPConfig *AFPConfigInit(struct afp_options *options,
484 const struct afp_options *defoptions)
486 AFPConfig *config = NULL, *next = NULL;
487 unsigned char *refcount;
489 if ((refcount = (unsigned char *)
490 calloc(1, sizeof(unsigned char))) == NULL) {
491 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
496 /* handle asp transports */
497 if ((options->transports & AFPTRANS_DDP) &&
498 (config = ASPConfigInit(options, refcount)))
499 config->defoptions = defoptions;
504 set_signature(options);
506 /* handle dsi transports and dsi proxies. we only proxy
507 * for DSI connections. */
509 /* this should have something like the following:
510 * for (i=mindsi; i < maxdsi; i++)
511 * if (options->transports & (1 << i) &&
512 * (next = DSIConfigInit(options, refcount, i)))
513 * next->defoptions = defoptions;
515 if ((options->transports & AFPTRANS_TCP) &&
516 (((options->flags & OPTION_PROXY) == 0) ||
517 ((options->flags & OPTION_PROXY) && config))
518 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
519 next->defoptions = defoptions;
521 /* load in all the authentication modules. we can load the same
522 things multiple times if necessary. however, loading different
523 things with the same names will cause complaints. by not loading
524 in any uams with proxies, we prevent ddp connections from succeeding.
526 auth_load(options->uampath, options->uamlist);
528 /* this should be able to accept multiple dsi transports. i think
529 * the only thing that gets affected is the net addresses. */
530 status_init(config, next, options);
532 /* attach dsi config to tail of asp config */
541 /* fill in the appropriate bits for each interface */
542 AFPConfig *configinit(struct afp_options *cmdline)
545 char buf[LINESIZE + 1], *p, have_option = 0;
547 struct afp_options options;
548 AFPConfig *config=NULL, *first = NULL;
551 /* Parse afp_ldap.conf first so we can set the uuid option */
552 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
553 #endif /* HAVE_ACLS */
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 /* scan in the configuration file */
566 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
569 if ( len >= 2 && buf[len-2] == '\\' ) {
575 /* a little pre-processing to get rid of spaces and end-of-lines */
577 while (p && isspace(*p))
579 if (!p || (*p == '\0'))
584 memcpy(&options, cmdline, sizeof(options));
585 if (!afp_options_parseline(p, &options))
589 /* Enable UUID support if LDAP config is complete */
590 if (ldap_config_valid) {
591 LOG(log_info, logtype_afpd, "Enabling UUID support");
592 options.flags |= OPTION_UUID;
594 #endif /* HAVE_ACLS */
596 /* AFPConfigInit can return two linked configs due to DSI and ASP */
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);
611 /* Now register with zeroconf, we also need the volumes for that */
612 load_volumes(&first->obj);
613 zeroconf_register(first);