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 */
159 static void dsi_cleanup(const AFPConfig *config)
162 SLPError callbackerr;
164 DSI *dsi = (DSI *)config->obj.handle;
166 /* Do nothing if we didn't register. */
167 if (!dsi || dsi->srvloc_url[0] == '\0')
170 err = SLPOpen("en", SLP_FALSE, &hslp);
172 LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
173 goto srvloc_dereg_err;
181 LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
182 goto srvloc_dereg_err;
185 if (callbackerr != SLP_OK) {
186 LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
187 goto srvloc_dereg_err;
191 dsi->srvloc_url[0] = '\0';
194 #endif /* USE_SRVLOC */
197 static void asp_cleanup(const AFPConfig *config)
199 /* we need to stop tickle handler */
201 nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
202 &config->obj.options.ddpaddr);
205 /* these two are almost identical. it should be possible to collapse them
206 * into one with minimal junk. */
207 static int asp_start(AFPConfig *config, AFPConfig *configs,
208 server_child *server_children)
212 if (!(asp = asp_getsession(config->obj.handle, server_children,
213 config->obj.options.tickleval))) {
214 LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
215 exit( EXITERR_CLNT );
219 configfree(configs, config); /* free a bunch of stuff */
220 afp_over_asp(&config->obj);
226 #endif /* no afp/asp */
228 static int dsi_start(AFPConfig *config, AFPConfig *configs,
229 server_child *server_children)
233 if (!(dsi = dsi_getsession(config->obj.handle, server_children,
234 config->obj.options.tickleval))) {
235 LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
236 exit( EXITERR_CLNT );
241 configfree(configs, config);
242 afp_over_dsi(&config->obj); /* start a session */
250 static AFPConfig *ASPConfigInit(const struct afp_options *options,
251 unsigned char *refcount)
256 char *Obj, *Type = "AFPServer", *Zone = "*";
257 char *convname = NULL;
259 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
262 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
263 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
268 if ((asp = asp_init( atp )) == NULL) {
269 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
275 /* register asp server */
276 Obj = (char *) options->hostname;
277 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
278 options->server, strlen(options->server), &convname)) ) {
279 if ((convname = strdup(options->server)) == NULL ) {
280 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
281 goto serv_free_return;
285 if (nbp_name(convname, &Obj, &Type, &Zone )) {
286 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
287 goto serv_free_return;
292 /* dup Obj, Type and Zone as they get assigned to a single internal
293 * buffer by nbp_name */
294 if ((config->obj.Obj = strdup(Obj)) == NULL)
295 goto serv_free_return;
297 if ((config->obj.Type = strdup(Type)) == NULL) {
298 free(config->obj.Obj);
299 goto serv_free_return;
302 if ((config->obj.Zone = strdup(Zone)) == NULL) {
303 free(config->obj.Obj);
304 free(config->obj.Type);
305 goto serv_free_return;
308 /* make sure we're not registered */
309 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
310 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
311 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
312 free(config->obj.Obj);
313 free(config->obj.Type);
314 free(config->obj.Zone);
315 goto serv_free_return;
318 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
319 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
320 atp_sockaddr( atp )->sat_addr.s_node,
321 atp_sockaddr( atp )->sat_port, VERSION );
323 config->fd = atp_fileno(atp);
324 config->obj.handle = asp;
325 config->obj.config = config;
326 config->obj.proto = AFPPROTO_ASP;
328 memcpy(&config->obj.options, options, sizeof(struct afp_options));
329 config->optcount = refcount;
332 config->server_start = asp_start;
333 config->server_cleanup = asp_cleanup;
342 #endif /* no afp/asp */
345 static AFPConfig *DSIConfigInit(const struct afp_options *options,
346 unsigned char *refcount,
347 const dsi_proto protocol)
353 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
354 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
358 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
360 options->ipaddr ? options->ipaddr : "default",
361 options->port ? options->port : "548");
363 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
364 options->ipaddr, options->port,
365 options->flags & OPTION_PROXY,
366 options->server_quantum)) == NULL) {
367 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
372 if (options->flags & OPTION_PROXY) {
373 LOG(log_info, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
374 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
376 LOG(log_info, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
377 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
381 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
382 if (!(options->flags & OPTION_NOSLP)) {
384 SLPError callbackerr;
386 unsigned int afp_port;
388 char *srvloc_hostname;
389 const char *hostname;
391 err = SLPOpen("en", SLP_FALSE, &hslp);
393 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
397 /* XXX We don't want to tack on the port number if we don't have to.
399 * Well, this seems to break MacOS < 10. If the user _really_ wants to
400 * use a non-default port, they can, but be aware, this server might
401 * not show up int the Network Browser.
403 afp_port = getip_port((struct sockaddr *)&dsi->server);
404 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
407 hostname = options->fqdn;
408 p = strchr(hostname, ':');
411 hostname = getip_string((struct sockaddr *)&dsi->server);
413 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
415 if ((p) || afp_port == 548) {
416 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
419 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
422 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
423 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
424 dsi->srvloc_url[0] = '\0';
430 SLP_LIFETIME_MAXIMUM,
437 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
438 dsi->srvloc_url[0] = '\0';
442 if (callbackerr != SLP_OK) {
443 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
444 dsi->srvloc_url[0] = '\0';
448 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
449 config->server_cleanup = dsi_cleanup;
454 #endif /* USE_SRVLOC */
457 config->fd = dsi->serversock;
458 config->obj.handle = dsi;
459 config->obj.config = config;
460 config->obj.proto = AFPPROTO_DSI;
462 memcpy(&config->obj.options, options, sizeof(struct afp_options));
463 /* get rid of any appletalk info. we use the fact that the DSI
464 * stuff is done after the ASP stuff. */
465 p = config->obj.options.server;
466 if (p && (q = strchr(p, ':')))
469 config->optcount = refcount;
472 config->server_start = dsi_start;
476 /* allocate server configurations. this should really store the last
477 * entry in config->last or something like that. that would make
478 * supporting multiple dsi transports easier. */
479 static AFPConfig *AFPConfigInit(const struct afp_options *options,
480 const struct afp_options *defoptions)
482 AFPConfig *config = NULL, *next = NULL;
483 unsigned char *refcount;
485 if ((refcount = (unsigned char *)
486 calloc(1, sizeof(unsigned char))) == NULL) {
487 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
492 /* handle asp transports */
493 if ((options->transports & AFPTRANS_DDP) &&
494 (config = ASPConfigInit(options, refcount)))
495 config->defoptions = defoptions;
500 set_signature(options);
502 /* handle dsi transports and dsi proxies. we only proxy
503 * for DSI connections. */
505 /* this should have something like the following:
506 * for (i=mindsi; i < maxdsi; i++)
507 * if (options->transports & (1 << i) &&
508 * (next = DSIConfigInit(options, refcount, i)))
509 * next->defoptions = defoptions;
511 if ((options->transports & AFPTRANS_TCP) &&
512 (((options->flags & OPTION_PROXY) == 0) ||
513 ((options->flags & OPTION_PROXY) && config))
514 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
515 next->defoptions = defoptions;
517 /* load in all the authentication modules. we can load the same
518 things multiple times if necessary. however, loading different
519 things with the same names will cause complaints. by not loading
520 in any uams with proxies, we prevent ddp connections from succeeding.
522 auth_load(options->uampath, options->uamlist);
524 /* this should be able to accept multiple dsi transports. i think
525 * the only thing that gets affected is the net addresses. */
526 status_init(config, next, options);
528 /* attach dsi config to tail of asp config */
537 /* fill in the appropriate bits for each interface */
538 AFPConfig *configinit(struct afp_options *cmdline)
541 char buf[LINESIZE + 1], *p, have_option = 0;
543 struct afp_options options;
544 AFPConfig *config=NULL, *first = NULL;
547 /* Parse afp_ldap.conf first so we can set the uuid option */
548 LOG(log_debug, logtype_afpd, "Start parsing afp_ldap.conf");
549 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
550 LOG(log_debug, logtype_afpd, "Finished parsing afp_ldap.conf");
551 #endif /* HAVE_ACLS */
553 /* if config file doesn't exist, load defaults */
554 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
556 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
557 cmdline->configfile);
558 return AFPConfigInit(cmdline, cmdline);
561 LOG(log_debug, logtype_afpd, "Loading ConfigFile");
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 options.flags |= OPTION_UUID;
592 #endif /* HAVE_ACLS */
594 /* this should really get a head and a tail to simplify things. */
596 if ((first = AFPConfigInit(&options, cmdline)))
597 config = first->next ? first->next : first;
598 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
599 config = config->next->next ? config->next->next : config->next;
603 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
607 first = AFPConfigInit(cmdline, cmdline);