2 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
17 #include <sys/socket.h>
18 #include <netinet/in.h>
19 #include <arpa/inet.h>
23 #endif /* USE_SRVLOC */
25 #include <atalk/logger.h>
26 #include <atalk/util.h>
27 #include <atalk/dsi.h>
28 #include <atalk/atp.h>
29 #include <atalk/asp.h>
30 #include <atalk/nbp.h>
31 #include <atalk/afp.h>
32 #include <atalk/compat.h>
33 #include <atalk/server_child.h>
36 #include <atalk/ldapconfig.h>
40 #include "afp_config.h"
44 #include "afp_zeroconf.h"
48 /* get rid of unneeded configurations. i use reference counts to deal
49 * w/ multiple configs sharing the same afp_options. oh, to dream of
50 * garbage collection ... */
51 void configfree(AFPConfig *configs, const AFPConfig *config)
55 for (p = configs; p; p = q) {
60 /* do a little reference counting */
61 if (--(*p->optcount) < 1) {
62 afp_options_free(&p->obj.options, p->defoptions);
66 switch (p->obj.proto) {
72 atp_close(((ASP) p->obj.handle)->asp_atp);
75 #endif /* no afp/asp */
84 /* the master loaded the volumes for zeroconf, get rid of that */
85 unload_volumes_and_extmap();
89 static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) {
90 *(SLPError*)cookie = errcode;
93 static char hex[17] = "0123456789abcdef";
95 static char * srvloc_encode(const struct afp_options *options, const char *name)
102 char *Obj, *Type = "", *Zone = "";
105 /* Convert name to maccharset */
106 if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
107 name, -1, &conv_name)) )
110 /* Escape characters */
112 while (*p && i<(sizeof(buf)-4)) {
115 else if (isspace(*p)) {
121 else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
123 buf[i++] = hex[*p >> 4];
124 buf[i++] = hex[*p++ & 15];
134 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
135 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
138 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
145 #endif /* USE_SRVLOC */
147 static void dsi_cleanup(const AFPConfig *config)
151 SLPError callbackerr;
153 DSI *dsi = (DSI *)config->obj.handle;
155 /* Do nothing if we didn't register. */
156 if (!dsi || dsi->srvloc_url[0] == '\0')
159 err = SLPOpen("en", SLP_FALSE, &hslp);
161 LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
162 goto srvloc_dereg_err;
170 LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
171 goto srvloc_dereg_err;
174 if (callbackerr != SLP_OK) {
175 LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
176 goto srvloc_dereg_err;
180 dsi->srvloc_url[0] = '\0';
182 #endif /* USE_SRVLOC */
186 static void asp_cleanup(const AFPConfig *config)
188 /* we need to stop tickle handler */
190 nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
191 &config->obj.options.ddpaddr);
194 /* these two are almost identical. it should be possible to collapse them
195 * into one with minimal junk. */
196 static int asp_start(AFPConfig *config, AFPConfig *configs,
197 server_child *server_children)
201 if (!(asp = asp_getsession(config->obj.handle, server_children,
202 config->obj.options.tickleval))) {
203 LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
204 exit( EXITERR_CLNT );
208 configfree(configs, config); /* free a bunch of stuff */
209 afp_over_asp(&config->obj);
215 #endif /* no afp/asp */
217 static int dsi_start(AFPConfig *config, AFPConfig *configs,
218 server_child *server_children)
222 if (!(dsi = dsi_getsession(config->obj.handle, server_children,
223 config->obj.options.tickleval))) {
224 LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
225 exit( EXITERR_CLNT );
230 configfree(configs, config);
231 afp_over_dsi(&config->obj); /* start a session */
239 static AFPConfig *ASPConfigInit(const struct afp_options *options,
240 unsigned char *refcount)
245 char *Obj, *Type = "AFPServer", *Zone = "*";
246 char *convname = NULL;
248 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
251 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
252 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
257 if ((asp = asp_init( atp )) == NULL) {
258 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
264 /* register asp server */
265 Obj = (char *) options->hostname;
266 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
267 options->server, strlen(options->server), &convname)) ) {
268 if ((convname = strdup(options->server)) == NULL ) {
269 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
270 goto serv_free_return;
274 if (nbp_name(convname, &Obj, &Type, &Zone )) {
275 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
276 goto serv_free_return;
281 /* dup Obj, Type and Zone as they get assigned to a single internal
282 * buffer by nbp_name */
283 if ((config->obj.Obj = strdup(Obj)) == NULL)
284 goto serv_free_return;
286 if ((config->obj.Type = strdup(Type)) == NULL) {
287 free(config->obj.Obj);
288 goto serv_free_return;
291 if ((config->obj.Zone = strdup(Zone)) == NULL) {
292 free(config->obj.Obj);
293 free(config->obj.Type);
294 goto serv_free_return;
297 /* make sure we're not registered */
298 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
299 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
300 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
301 free(config->obj.Obj);
302 free(config->obj.Type);
303 free(config->obj.Zone);
304 goto serv_free_return;
307 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
308 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
309 atp_sockaddr( atp )->sat_addr.s_node,
310 atp_sockaddr( atp )->sat_port, VERSION );
312 config->fd = atp_fileno(atp);
313 config->obj.handle = asp;
314 config->obj.config = config;
315 config->obj.proto = AFPPROTO_ASP;
317 memcpy(&config->obj.options, options, sizeof(struct afp_options));
318 config->optcount = refcount;
321 config->server_start = asp_start;
322 config->server_cleanup = asp_cleanup;
331 #endif /* no afp/asp */
334 static AFPConfig *DSIConfigInit(const struct afp_options *options,
335 unsigned char *refcount,
336 const dsi_proto protocol)
342 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
343 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
347 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
349 options->ipaddr ? options->ipaddr : "default",
350 options->port ? options->port : "548");
352 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
353 options->ipaddr, options->port,
354 options->flags & OPTION_PROXY,
355 options->server_quantum)) == NULL) {
356 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
361 if (options->flags & OPTION_PROXY) {
362 LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
363 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
365 LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
366 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
370 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
371 if (!(options->flags & OPTION_NOSLP)) {
373 SLPError callbackerr;
375 unsigned int afp_port;
377 char *srvloc_hostname;
378 const char *hostname;
380 err = SLPOpen("en", SLP_FALSE, &hslp);
382 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
386 /* XXX We don't want to tack on the port number if we don't have to.
388 * Well, this seems to break MacOS < 10. If the user _really_ wants to
389 * use a non-default port, they can, but be aware, this server might
390 * not show up int the Network Browser.
392 afp_port = getip_port((struct sockaddr *)&dsi->server);
393 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
396 hostname = options->fqdn;
397 p = strchr(hostname, ':');
400 hostname = getip_string((struct sockaddr *)&dsi->server);
402 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
404 if ((p) || afp_port == 548) {
405 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
408 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
411 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
412 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
413 dsi->srvloc_url[0] = '\0';
419 SLP_LIFETIME_MAXIMUM,
426 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
427 dsi->srvloc_url[0] = '\0';
431 if (callbackerr != SLP_OK) {
432 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
433 dsi->srvloc_url[0] = '\0';
437 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
438 config->server_cleanup = dsi_cleanup;
443 #endif /* USE_SRVLOC */
445 config->fd = dsi->serversock;
446 config->obj.handle = dsi;
447 config->obj.config = config;
448 config->obj.proto = AFPPROTO_DSI;
450 memcpy(&config->obj.options, options, sizeof(struct afp_options));
451 /* get rid of any appletalk info. we use the fact that the DSI
452 * stuff is done after the ASP stuff. */
453 p = config->obj.options.server;
454 if (p && (q = strchr(p, ':')))
457 config->optcount = refcount;
460 config->server_start = dsi_start;
464 /* allocate server configurations. this should really store the last
465 * entry in config->last or something like that. that would make
466 * supporting multiple dsi transports easier. */
467 static AFPConfig *AFPConfigInit(struct afp_options *options,
468 const struct afp_options *defoptions)
470 AFPConfig *config = NULL, *next = NULL;
471 unsigned char *refcount;
473 if ((refcount = (unsigned char *)
474 calloc(1, sizeof(unsigned char))) == NULL) {
475 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
480 /* handle asp transports */
481 if ((options->transports & AFPTRANS_DDP) &&
482 (config = ASPConfigInit(options, refcount)))
483 config->defoptions = defoptions;
488 set_signature(options);
490 /* handle dsi transports and dsi proxies. we only proxy
491 * for DSI connections. */
493 /* this should have something like the following:
494 * for (i=mindsi; i < maxdsi; i++)
495 * if (options->transports & (1 << i) &&
496 * (next = DSIConfigInit(options, refcount, i)))
497 * next->defoptions = defoptions;
499 if ((options->transports & AFPTRANS_TCP) &&
500 (((options->flags & OPTION_PROXY) == 0) ||
501 ((options->flags & OPTION_PROXY) && config))
502 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
503 next->defoptions = defoptions;
505 /* load in all the authentication modules. we can load the same
506 things multiple times if necessary. however, loading different
507 things with the same names will cause complaints. by not loading
508 in any uams with proxies, we prevent ddp connections from succeeding.
510 auth_load(options->uampath, options->uamlist);
512 /* this should be able to accept multiple dsi transports. i think
513 * the only thing that gets affected is the net addresses. */
514 status_init(config, next, options);
516 /* attach dsi config to tail of asp config */
525 /* fill in the appropriate bits for each interface */
526 AFPConfig *configinit(struct afp_options *cmdline)
529 char buf[LINESIZE + 1], *p, have_option = 0;
531 struct afp_options options;
532 AFPConfig *config=NULL, *first = NULL;
534 /* if config file doesn't exist, load defaults */
535 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
537 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
538 cmdline->configfile);
539 return AFPConfigInit(cmdline, cmdline);
542 /* scan in the configuration file */
545 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
548 if ( len >= 2 && buf[len-2] == '\\' ) {
554 /* a little pre-processing to get rid of spaces and end-of-lines */
556 while (p && isspace(*p))
558 if (!p || (*p == '\0'))
563 memcpy(&options, cmdline, sizeof(options));
564 if (!afp_options_parseline(p, &options))
567 /* AFPConfigInit can return two linked configs due to DSI and ASP */
569 if ((first = AFPConfigInit(&options, cmdline)))
570 config = first->next ? first->next : first;
571 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
572 config = config->next->next ? config->next->next : config->next;
577 /* Parse afp_ldap.conf */
578 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
579 #endif /* HAVE_LDAP */
581 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
585 first = AFPConfigInit(cmdline, cmdline);
587 /* Now register with zeroconf, we also need the volumes for that */
588 load_volumes(&first->obj);
589 zeroconf_register(first);