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 */
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 */
160 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';
195 #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 */
458 config->fd = dsi->serversock;
459 config->obj.handle = dsi;
460 config->obj.config = config;
461 config->obj.proto = AFPPROTO_DSI;
463 memcpy(&config->obj.options, options, sizeof(struct afp_options));
464 /* get rid of any appletalk info. we use the fact that the DSI
465 * stuff is done after the ASP stuff. */
466 p = config->obj.options.server;
467 if (p && (q = strchr(p, ':')))
470 config->optcount = refcount;
473 config->server_start = dsi_start;
477 /* allocate server configurations. this should really store the last
478 * entry in config->last or something like that. that would make
479 * supporting multiple dsi transports easier. */
480 static AFPConfig *AFPConfigInit(struct afp_options *options,
481 const struct afp_options *defoptions)
483 AFPConfig *config = NULL, *next = NULL;
484 unsigned char *refcount;
486 if ((refcount = (unsigned char *)
487 calloc(1, sizeof(unsigned char))) == NULL) {
488 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
493 /* handle asp transports */
494 if ((options->transports & AFPTRANS_DDP) &&
495 (config = ASPConfigInit(options, refcount)))
496 config->defoptions = defoptions;
501 set_signature(options);
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;
548 /* Parse afp_ldap.conf first so we can set the uuid option */
549 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
550 #endif /* HAVE_ACLS */
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 /* scan in the configuration file */
563 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
566 if ( len >= 2 && buf[len-2] == '\\' ) {
572 /* a little pre-processing to get rid of spaces and end-of-lines */
574 while (p && isspace(*p))
576 if (!p || (*p == '\0'))
581 memcpy(&options, cmdline, sizeof(options));
582 if (!afp_options_parseline(p, &options))
586 /* Enable UUID support if LDAP config is complete */
587 if (ldap_config_valid) {
588 LOG(log_info, logtype_afpd, "Enabling UUID support");
589 options.flags |= OPTION_UUID;
591 #endif /* HAVE_ACLS */
593 /* AFPConfigInit can return two linked configs due to DSI and ASP */
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);
608 /* Now register with zeroconf, we also need the volumes for that */
609 readvolfile(&first->obj, &first->obj.options.systemvol, NULL, 0, NULL);
611 zeroconf_register(first);