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"
62 /* get rid of unneeded configurations. i use reference counts to deal
63 * w/ multiple configs sharing the same afp_options. oh, to dream of
64 * garbage collection ... */
65 void configfree(AFPConfig *configs, const AFPConfig *config)
69 for (p = configs; p; p = q) {
74 /* do a little reference counting */
75 if (--(*p->optcount) < 1) {
76 afp_options_free(&p->obj.options, p->defoptions);
80 switch (p->obj.proto) {
86 atp_close(((ASP) p->obj.handle)->asp_atp);
89 #endif /* no afp/asp */
100 static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) {
101 *(SLPError*)cookie = errcode;
104 static char hex[17] = "0123456789abcdef";
106 static char * srvloc_encode(const struct afp_options *options, const char *name)
108 static char buf[512];
113 char *Obj, *Type = "", *Zone = "";
116 /* Convert name to maccharset */
117 if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
118 name, -1, &conv_name)) )
121 /* Escape characters */
123 while (*p && i<(sizeof(buf)-4)) {
126 else if (isspace(*p)) {
132 else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
134 buf[i++] = hex[*p >> 4];
135 buf[i++] = hex[*p++ & 15];
145 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
146 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
149 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
156 #endif /* USE_SRVLOC */
158 #include "afp_zeroconf.h"
159 #endif /* USE_ZEROCONF */
161 static void dsi_cleanup(const AFPConfig *config)
165 SLPError callbackerr;
167 DSI *dsi = (DSI *)config->obj.handle;
169 /* Do nothing if we didn't register. */
170 if (!dsi || dsi->srvloc_url[0] == '\0')
173 err = SLPOpen("en", SLP_FALSE, &hslp);
175 LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
176 goto srvloc_dereg_err;
184 LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
185 goto srvloc_dereg_err;
188 if (callbackerr != SLP_OK) {
189 LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
190 goto srvloc_dereg_err;
194 dsi->srvloc_url[0] = '\0';
196 #elif defined (USE_ZEROCONF)
197 DSI *dsi = (DSI *)config->obj.handle;
199 /* Do nothing if we didn't register. */
200 if (!dsi || dsi->zeroconf_registered == 0)
203 zeroconf_deregister();
204 #endif /* USE_SRVLOC */
208 static void asp_cleanup(const AFPConfig *config)
210 /* we need to stop tickle handler */
212 nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
213 &config->obj.options.ddpaddr);
216 /* these two are almost identical. it should be possible to collapse them
217 * into one with minimal junk. */
218 static int asp_start(AFPConfig *config, AFPConfig *configs,
219 server_child *server_children)
223 if (!(asp = asp_getsession(config->obj.handle, server_children,
224 config->obj.options.tickleval))) {
225 LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
226 exit( EXITERR_CLNT );
230 configfree(configs, config); /* free a bunch of stuff */
231 afp_over_asp(&config->obj);
237 #endif /* no afp/asp */
239 static int dsi_start(AFPConfig *config, AFPConfig *configs,
240 server_child *server_children)
244 if (!(dsi = dsi_getsession(config->obj.handle, server_children,
245 config->obj.options.tickleval))) {
246 LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
247 exit( EXITERR_CLNT );
252 configfree(configs, config);
253 afp_over_dsi(&config->obj); /* start a session */
261 static AFPConfig *ASPConfigInit(const struct afp_options *options,
262 unsigned char *refcount)
267 char *Obj, *Type = "AFPServer", *Zone = "*";
268 char *convname = NULL;
270 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
273 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
274 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
279 if ((asp = asp_init( atp )) == NULL) {
280 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
286 /* register asp server */
287 Obj = (char *) options->hostname;
288 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
289 options->server, strlen(options->server), &convname)) ) {
290 if ((convname = strdup(options->server)) == NULL ) {
291 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
292 goto serv_free_return;
296 if (nbp_name(convname, &Obj, &Type, &Zone )) {
297 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
298 goto serv_free_return;
303 /* dup Obj, Type and Zone as they get assigned to a single internal
304 * buffer by nbp_name */
305 if ((config->obj.Obj = strdup(Obj)) == NULL)
306 goto serv_free_return;
308 if ((config->obj.Type = strdup(Type)) == NULL) {
309 free(config->obj.Obj);
310 goto serv_free_return;
313 if ((config->obj.Zone = strdup(Zone)) == NULL) {
314 free(config->obj.Obj);
315 free(config->obj.Type);
316 goto serv_free_return;
319 /* make sure we're not registered */
320 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
321 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
322 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
323 free(config->obj.Obj);
324 free(config->obj.Type);
325 free(config->obj.Zone);
326 goto serv_free_return;
329 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
330 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
331 atp_sockaddr( atp )->sat_addr.s_node,
332 atp_sockaddr( atp )->sat_port, VERSION );
334 config->fd = atp_fileno(atp);
335 config->obj.handle = asp;
336 config->obj.config = config;
337 config->obj.proto = AFPPROTO_ASP;
339 memcpy(&config->obj.options, options, sizeof(struct afp_options));
340 config->optcount = refcount;
343 config->server_start = asp_start;
344 config->server_cleanup = asp_cleanup;
353 #endif /* no afp/asp */
356 static AFPConfig *DSIConfigInit(const struct afp_options *options,
357 unsigned char *refcount,
358 const dsi_proto protocol)
364 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
365 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
369 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
371 options->ipaddr ? options->ipaddr : "default",
372 options->port ? options->port : "548");
374 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
375 options->ipaddr, options->port,
376 options->flags & OPTION_PROXY,
377 options->server_quantum)) == NULL) {
378 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
383 if (options->flags & OPTION_PROXY) {
384 LOG(log_info, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
385 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
387 LOG(log_info, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
388 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
392 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
393 if (!(options->flags & OPTION_NOSLP)) {
395 SLPError callbackerr;
397 unsigned int afp_port;
399 char *srvloc_hostname;
400 const char *hostname;
402 err = SLPOpen("en", SLP_FALSE, &hslp);
404 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
408 /* XXX We don't want to tack on the port number if we don't have to.
410 * Well, this seems to break MacOS < 10. If the user _really_ wants to
411 * use a non-default port, they can, but be aware, this server might
412 * not show up int the Network Browser.
414 afp_port = getip_port((struct sockaddr *)&dsi->server);
415 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
418 hostname = options->fqdn;
419 p = strchr(hostname, ':');
422 hostname = getip_string((struct sockaddr *)&dsi->server);
424 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
426 if ((p) || afp_port == 548) {
427 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
430 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
433 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
434 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
435 dsi->srvloc_url[0] = '\0';
441 SLP_LIFETIME_MAXIMUM,
448 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
449 dsi->srvloc_url[0] = '\0';
453 if (callbackerr != SLP_OK) {
454 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
455 dsi->srvloc_url[0] = '\0';
459 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
460 config->server_cleanup = dsi_cleanup;
465 #endif /* USE_SRVLOC */
469 const char *hostname = NULL;
471 dsi->zeroconf_registered = 0; /* Mark that we haven't registered. */
473 if (!(options->flags & OPTION_NOZEROCONF)) {
474 /* XXX We don't want to tack on the port number if we don't have to.
476 * Well, this seems to break MacOS < 10. If the user _really_ wants to
477 * use a non-default port, they can, but be aware, this server might
478 * not show up int the Network Browser.
480 afp_port = getip_port((struct sockaddr *)&dsi->server);
482 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
485 hostname = options->fqdn;
486 p = strchr(hostname, ':');
488 hostname = getip_string((struct sockaddr *)&dsi->server);
491 zeroconf_register(afp_port, hostname);
492 dsi->zeroconf_registered = 1; /* Mark that we have registered. */
493 config->server_cleanup = dsi_cleanup;
495 #endif /* USE_ZEROCONF */
497 config->fd = dsi->serversock;
498 config->obj.handle = dsi;
499 config->obj.config = config;
500 config->obj.proto = AFPPROTO_DSI;
502 memcpy(&config->obj.options, options, sizeof(struct afp_options));
503 /* get rid of any appletalk info. we use the fact that the DSI
504 * stuff is done after the ASP stuff. */
505 p = config->obj.options.server;
506 if (p && (q = strchr(p, ':')))
509 config->optcount = refcount;
512 config->server_start = dsi_start;
516 /* allocate server configurations. this should really store the last
517 * entry in config->last or something like that. that would make
518 * supporting multiple dsi transports easier. */
519 static AFPConfig *AFPConfigInit(const struct afp_options *options,
520 const struct afp_options *defoptions)
522 AFPConfig *config = NULL, *next = NULL;
523 unsigned char *refcount;
525 if ((refcount = (unsigned char *)
526 calloc(1, sizeof(unsigned char))) == NULL) {
527 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
532 /* handle asp transports */
533 if ((options->transports & AFPTRANS_DDP) &&
534 (config = ASPConfigInit(options, refcount)))
535 config->defoptions = defoptions;
540 set_signature(options);
542 /* handle dsi transports and dsi proxies. we only proxy
543 * for DSI connections. */
545 /* this should have something like the following:
546 * for (i=mindsi; i < maxdsi; i++)
547 * if (options->transports & (1 << i) &&
548 * (next = DSIConfigInit(options, refcount, i)))
549 * next->defoptions = defoptions;
551 if ((options->transports & AFPTRANS_TCP) &&
552 (((options->flags & OPTION_PROXY) == 0) ||
553 ((options->flags & OPTION_PROXY) && config))
554 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
555 next->defoptions = defoptions;
557 /* load in all the authentication modules. we can load the same
558 things multiple times if necessary. however, loading different
559 things with the same names will cause complaints. by not loading
560 in any uams with proxies, we prevent ddp connections from succeeding.
562 auth_load(options->uampath, options->uamlist);
564 /* this should be able to accept multiple dsi transports. i think
565 * the only thing that gets affected is the net addresses. */
566 status_init(config, next, options);
568 /* attach dsi config to tail of asp config */
577 /* fill in the appropriate bits for each interface */
578 AFPConfig *configinit(struct afp_options *cmdline)
581 char buf[LINESIZE + 1], *p, have_option = 0;
583 struct afp_options options;
584 AFPConfig *config=NULL, *first = NULL;
587 /* Parse afp_ldap.conf first so we can set the uuid option */
588 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
589 #endif /* HAVE_ACLS */
591 /* if config file doesn't exist, load defaults */
592 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
594 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
595 cmdline->configfile);
596 return AFPConfigInit(cmdline, cmdline);
599 /* scan in the configuration file */
602 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
605 if ( len >= 2 && buf[len-2] == '\\' ) {
611 /* a little pre-processing to get rid of spaces and end-of-lines */
613 while (p && isspace(*p))
615 if (!p || (*p == '\0'))
620 memcpy(&options, cmdline, sizeof(options));
621 if (!afp_options_parseline(p, &options))
625 /* Enable UUID support if LDAP config is complete */
626 if (ldap_config_valid) {
627 LOG(log_info, logtype_afpd, "Enabling UUID support");
628 options.flags |= OPTION_UUID;
630 #endif /* HAVE_ACLS */
632 /* this should really get a head and a tail to simplify things. */
634 if ((first = AFPConfigInit(&options, cmdline)))
635 config = first->next ? first->next : first;
636 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
637 config = config->next->next ? config->next->next : config->next;
641 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
645 first = AFPConfigInit(cmdline, cmdline);