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>
39 #include <atalk/globals.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];
235 afp_over_dsi(&config->obj); /* start a session */
243 static AFPConfig *ASPConfigInit(const struct afp_options *options,
244 unsigned char *refcount)
249 char *Obj, *Type = "AFPServer", *Zone = "*";
250 char *convname = NULL;
252 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
255 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
256 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
261 if ((asp = asp_init( atp )) == NULL) {
262 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
268 /* register asp server */
269 Obj = (char *) options->hostname;
270 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
271 options->server, strlen(options->server), &convname)) ) {
272 if ((convname = strdup(options->server)) == NULL ) {
273 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
274 goto serv_free_return;
278 if (nbp_name(convname, &Obj, &Type, &Zone )) {
279 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
280 goto serv_free_return;
285 /* dup Obj, Type and Zone as they get assigned to a single internal
286 * buffer by nbp_name */
287 if ((config->obj.Obj = strdup(Obj)) == NULL)
288 goto serv_free_return;
290 if ((config->obj.Type = strdup(Type)) == NULL) {
291 free(config->obj.Obj);
292 goto serv_free_return;
295 if ((config->obj.Zone = strdup(Zone)) == NULL) {
296 free(config->obj.Obj);
297 free(config->obj.Type);
298 goto serv_free_return;
301 /* make sure we're not registered */
302 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
303 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
304 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
305 free(config->obj.Obj);
306 free(config->obj.Type);
307 free(config->obj.Zone);
308 goto serv_free_return;
311 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
312 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
313 atp_sockaddr( atp )->sat_addr.s_node,
314 atp_sockaddr( atp )->sat_port, VERSION );
316 config->fd = atp_fileno(atp);
317 config->obj.handle = asp;
318 config->obj.config = config;
319 config->obj.proto = AFPPROTO_ASP;
321 memcpy(&config->obj.options, options, sizeof(struct afp_options));
322 config->optcount = refcount;
325 config->server_start = asp_start;
326 config->server_cleanup = asp_cleanup;
335 #endif /* no afp/asp */
338 static AFPConfig *DSIConfigInit(const struct afp_options *options,
339 unsigned char *refcount,
340 const dsi_proto protocol)
346 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
347 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
351 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
353 options->ipaddr ? options->ipaddr : "default",
354 options->port ? options->port : "548");
356 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
357 options->ipaddr, options->port,
358 options->flags & OPTION_PROXY,
359 options->server_quantum)) == NULL) {
360 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
364 dsi->dsireadbuf = options->dsireadbuf;
366 if (options->flags & OPTION_PROXY) {
367 LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
368 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
370 LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
371 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
375 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
376 if (!(options->flags & OPTION_NOSLP)) {
378 SLPError callbackerr;
380 unsigned int afp_port;
382 char *srvloc_hostname;
383 const char *hostname;
385 err = SLPOpen("en", SLP_FALSE, &hslp);
387 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
391 /* XXX We don't want to tack on the port number if we don't have to.
393 * Well, this seems to break MacOS < 10. If the user _really_ wants to
394 * use a non-default port, they can, but be aware, this server might
395 * not show up int the Network Browser.
397 afp_port = getip_port((struct sockaddr *)&dsi->server);
398 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
401 hostname = options->fqdn;
402 p = strchr(hostname, ':');
405 hostname = getip_string((struct sockaddr *)&dsi->server);
407 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
409 if ((p) || afp_port == 548) {
410 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
413 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
416 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
417 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
418 dsi->srvloc_url[0] = '\0';
424 SLP_LIFETIME_MAXIMUM,
431 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
432 dsi->srvloc_url[0] = '\0';
436 if (callbackerr != SLP_OK) {
437 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
438 dsi->srvloc_url[0] = '\0';
442 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
443 config->server_cleanup = dsi_cleanup;
448 #endif /* USE_SRVLOC */
450 config->fd = dsi->serversock;
451 config->obj.handle = dsi;
452 config->obj.config = config;
453 config->obj.proto = AFPPROTO_DSI;
455 memcpy(&config->obj.options, options, sizeof(struct afp_options));
456 /* get rid of any appletalk info. we use the fact that the DSI
457 * stuff is done after the ASP stuff. */
458 p = config->obj.options.server;
459 if (p && (q = strchr(p, ':')))
462 config->optcount = refcount;
465 config->server_start = dsi_start;
469 /* allocate server configurations. this should really store the last
470 * entry in config->last or something like that. that would make
471 * supporting multiple dsi transports easier. */
472 static AFPConfig *AFPConfigInit(struct afp_options *options,
473 const struct afp_options *defoptions)
475 AFPConfig *config = NULL, *next = NULL;
476 unsigned char *refcount;
478 if ((refcount = (unsigned char *)
479 calloc(1, sizeof(unsigned char))) == NULL) {
480 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
485 /* handle asp transports */
486 if ((options->transports & AFPTRANS_DDP) &&
487 (config = ASPConfigInit(options, refcount)))
488 config->defoptions = defoptions;
493 set_signature(options);
495 /* handle dsi transports and dsi proxies. we only proxy
496 * for DSI connections. */
498 /* this should have something like the following:
499 * for (i=mindsi; i < maxdsi; i++)
500 * if (options->transports & (1 << i) &&
501 * (next = DSIConfigInit(options, refcount, i)))
502 * next->defoptions = defoptions;
504 if ((options->transports & AFPTRANS_TCP) &&
505 (((options->flags & OPTION_PROXY) == 0) ||
506 ((options->flags & OPTION_PROXY) && config))
507 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
508 next->defoptions = defoptions;
510 /* load in all the authentication modules. we can load the same
511 things multiple times if necessary. however, loading different
512 things with the same names will cause complaints. by not loading
513 in any uams with proxies, we prevent ddp connections from succeeding.
515 auth_load(options->uampath, options->uamlist);
517 /* this should be able to accept multiple dsi transports. i think
518 * the only thing that gets affected is the net addresses. */
519 status_init(config, next, options);
521 /* attach dsi config to tail of asp config */
530 /* fill in the appropriate bits for each interface */
531 AFPConfig *configinit(struct afp_options *cmdline)
534 char buf[LINESIZE + 1], *p, have_option = 0;
536 struct afp_options options;
537 AFPConfig *config=NULL, *first = NULL;
539 /* if config file doesn't exist, load defaults */
540 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
542 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
543 cmdline->configfile);
544 return AFPConfigInit(cmdline, cmdline);
547 /* scan in the configuration file */
550 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
553 if ( len >= 2 && buf[len-2] == '\\' ) {
559 /* a little pre-processing to get rid of spaces and end-of-lines */
561 while (p && isspace(*p))
563 if (!p || (*p == '\0'))
568 memcpy(&options, cmdline, sizeof(options));
569 if (!afp_options_parseline(p, &options))
572 /* AFPConfigInit can return two linked configs due to DSI and ASP */
574 if ((first = AFPConfigInit(&options, cmdline)))
575 config = first->next ? first->next : first;
576 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
577 config = config->next->next ? config->next->next : config->next;
582 /* Parse afp_ldap.conf */
583 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
584 #endif /* HAVE_LDAP */
586 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
590 first = AFPConfigInit(cmdline, cmdline);
592 /* Now register with zeroconf, we also need the volumes for that */
593 if (! (first->obj.options.flags & OPTION_NOZEROCONF)) {
594 load_volumes(&first->obj);
595 zeroconf_register(first);