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 afp_child_t *dsi_start(AFPConfig *config, AFPConfig *configs,
218 server_child *server_children)
220 DSI *dsi = config->obj.handle;
221 afp_child_t *child = NULL;
223 if (!(child = dsi_getsession(dsi,
225 config->obj.options.tickleval))) {
226 LOG(log_error, logtype_afpd, "dsi_start: session error: %s", strerror(errno));
231 if (parent_or_child == 1) {
232 configfree(configs, config);
233 config->obj.ipc_fd = child->ipc_fds[1];
234 close(child->ipc_fds[0]); /* Close parent IPC fd */
236 afp_over_dsi(&config->obj); /* start a session */
244 static AFPConfig *ASPConfigInit(const struct afp_options *options,
245 unsigned char *refcount)
250 char *Obj, *Type = "AFPServer", *Zone = "*";
251 char *convname = NULL;
253 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
256 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
257 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
262 if ((asp = asp_init( atp )) == NULL) {
263 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
269 /* register asp server */
270 Obj = (char *) options->hostname;
271 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
272 options->server, strlen(options->server), &convname)) ) {
273 if ((convname = strdup(options->server)) == NULL ) {
274 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
275 goto serv_free_return;
279 if (nbp_name(convname, &Obj, &Type, &Zone )) {
280 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
281 goto serv_free_return;
286 /* dup Obj, Type and Zone as they get assigned to a single internal
287 * buffer by nbp_name */
288 if ((config->obj.Obj = strdup(Obj)) == NULL)
289 goto serv_free_return;
291 if ((config->obj.Type = strdup(Type)) == NULL) {
292 free(config->obj.Obj);
293 goto serv_free_return;
296 if ((config->obj.Zone = strdup(Zone)) == NULL) {
297 free(config->obj.Obj);
298 free(config->obj.Type);
299 goto serv_free_return;
302 /* make sure we're not registered */
303 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
304 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
305 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
306 free(config->obj.Obj);
307 free(config->obj.Type);
308 free(config->obj.Zone);
309 goto serv_free_return;
312 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
313 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
314 atp_sockaddr( atp )->sat_addr.s_node,
315 atp_sockaddr( atp )->sat_port, VERSION );
317 config->fd = atp_fileno(atp);
318 config->obj.handle = asp;
319 config->obj.config = config;
320 config->obj.proto = AFPPROTO_ASP;
322 memcpy(&config->obj.options, options, sizeof(struct afp_options));
323 config->optcount = refcount;
326 config->server_start = asp_start;
327 config->server_cleanup = asp_cleanup;
336 #endif /* no afp/asp */
339 static AFPConfig *DSIConfigInit(const struct afp_options *options,
340 unsigned char *refcount,
341 const dsi_proto protocol)
347 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
348 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
352 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
354 options->ipaddr ? options->ipaddr : "default",
355 options->port ? options->port : "548");
357 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
358 options->ipaddr, options->port,
359 options->flags & OPTION_PROXY,
360 options->server_quantum)) == NULL) {
361 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
365 dsi->dsireadbuf = options->dsireadbuf;
367 if (options->flags & OPTION_PROXY) {
368 LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
369 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
371 LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
372 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
376 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
377 if (!(options->flags & OPTION_NOSLP)) {
379 SLPError callbackerr;
381 unsigned int afp_port;
383 char *srvloc_hostname;
384 const char *hostname;
386 err = SLPOpen("en", SLP_FALSE, &hslp);
388 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
392 /* XXX We don't want to tack on the port number if we don't have to.
394 * Well, this seems to break MacOS < 10. If the user _really_ wants to
395 * use a non-default port, they can, but be aware, this server might
396 * not show up int the Network Browser.
398 afp_port = getip_port((struct sockaddr *)&dsi->server);
399 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
402 hostname = options->fqdn;
403 p = strchr(hostname, ':');
406 hostname = getip_string((struct sockaddr *)&dsi->server);
408 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
410 if ((p) || afp_port == 548) {
411 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
414 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
417 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
418 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
419 dsi->srvloc_url[0] = '\0';
425 SLP_LIFETIME_MAXIMUM,
432 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
433 dsi->srvloc_url[0] = '\0';
437 if (callbackerr != SLP_OK) {
438 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
439 dsi->srvloc_url[0] = '\0';
443 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
444 config->server_cleanup = dsi_cleanup;
449 #endif /* USE_SRVLOC */
451 config->fd = dsi->serversock;
452 config->obj.handle = dsi;
453 config->obj.config = config;
454 config->obj.proto = AFPPROTO_DSI;
456 memcpy(&config->obj.options, options, sizeof(struct afp_options));
457 /* get rid of any appletalk info. we use the fact that the DSI
458 * stuff is done after the ASP stuff. */
459 p = config->obj.options.server;
460 if (p && (q = strchr(p, ':')))
463 config->optcount = refcount;
466 config->server_start = dsi_start;
470 /* allocate server configurations. this should really store the last
471 * entry in config->last or something like that. that would make
472 * supporting multiple dsi transports easier. */
473 static AFPConfig *AFPConfigInit(struct afp_options *options,
474 const struct afp_options *defoptions)
476 AFPConfig *config = NULL, *next = NULL;
477 unsigned char *refcount;
479 if ((refcount = (unsigned char *)
480 calloc(1, sizeof(unsigned char))) == NULL) {
481 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
486 /* handle asp transports */
487 if ((options->transports & AFPTRANS_DDP) &&
488 (config = ASPConfigInit(options, refcount)))
489 config->defoptions = defoptions;
494 set_signature(options);
496 /* handle dsi transports and dsi proxies. we only proxy
497 * for DSI connections. */
499 /* this should have something like the following:
500 * for (i=mindsi; i < maxdsi; i++)
501 * if (options->transports & (1 << i) &&
502 * (next = DSIConfigInit(options, refcount, i)))
503 * next->defoptions = defoptions;
505 if ((options->transports & AFPTRANS_TCP) &&
506 (((options->flags & OPTION_PROXY) == 0) ||
507 ((options->flags & OPTION_PROXY) && config))
508 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
509 next->defoptions = defoptions;
511 /* load in all the authentication modules. we can load the same
512 things multiple times if necessary. however, loading different
513 things with the same names will cause complaints. by not loading
514 in any uams with proxies, we prevent ddp connections from succeeding.
516 auth_load(options->uampath, options->uamlist);
518 /* this should be able to accept multiple dsi transports. i think
519 * the only thing that gets affected is the net addresses. */
520 status_init(config, next, options);
522 /* attach dsi config to tail of asp config */
531 /* fill in the appropriate bits for each interface */
532 AFPConfig *configinit(struct afp_options *cmdline)
535 char buf[LINESIZE + 1], *p, have_option = 0;
537 struct afp_options options;
538 AFPConfig *config=NULL, *first = NULL;
540 /* if config file doesn't exist, load defaults */
541 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
543 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
544 cmdline->configfile);
545 return AFPConfigInit(cmdline, cmdline);
548 /* scan in the configuration file */
551 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
554 if ( len >= 2 && buf[len-2] == '\\' ) {
560 /* a little pre-processing to get rid of spaces and end-of-lines */
562 while (p && isspace(*p))
564 if (!p || (*p == '\0'))
569 memcpy(&options, cmdline, sizeof(options));
570 if (!afp_options_parseline(p, &options))
573 /* AFPConfigInit can return two linked configs due to DSI and ASP */
575 if ((first = AFPConfigInit(&options, cmdline)))
576 config = first->next ? first->next : first;
577 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
578 config = config->next->next ? config->next->next : config->next;
583 /* Parse afp_ldap.conf */
584 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
585 #endif /* HAVE_LDAP */
587 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
591 first = AFPConfigInit(cmdline, cmdline);
593 /* Now register with zeroconf, we also need the volumes for that */
594 if (! (first->obj.options.flags & OPTION_NOZEROCONF)) {
595 load_volumes(&first->obj);
596 zeroconf_register(first);