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))) {
227 configfree(configs, config);
228 afp_over_dsi(&config->obj); /* start a session */
236 static AFPConfig *ASPConfigInit(const struct afp_options *options,
237 unsigned char *refcount)
242 char *Obj, *Type = "AFPServer", *Zone = "*";
243 char *convname = NULL;
245 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
248 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
249 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
254 if ((asp = asp_init( atp )) == NULL) {
255 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
261 /* register asp server */
262 Obj = (char *) options->hostname;
263 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
264 options->server, strlen(options->server), &convname)) ) {
265 if ((convname = strdup(options->server)) == NULL ) {
266 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
267 goto serv_free_return;
271 if (nbp_name(convname, &Obj, &Type, &Zone )) {
272 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
273 goto serv_free_return;
278 /* dup Obj, Type and Zone as they get assigned to a single internal
279 * buffer by nbp_name */
280 if ((config->obj.Obj = strdup(Obj)) == NULL)
281 goto serv_free_return;
283 if ((config->obj.Type = strdup(Type)) == NULL) {
284 free(config->obj.Obj);
285 goto serv_free_return;
288 if ((config->obj.Zone = strdup(Zone)) == NULL) {
289 free(config->obj.Obj);
290 free(config->obj.Type);
291 goto serv_free_return;
294 /* make sure we're not registered */
295 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
296 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
297 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
298 free(config->obj.Obj);
299 free(config->obj.Type);
300 free(config->obj.Zone);
301 goto serv_free_return;
304 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
305 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
306 atp_sockaddr( atp )->sat_addr.s_node,
307 atp_sockaddr( atp )->sat_port, VERSION );
309 config->fd = atp_fileno(atp);
310 config->obj.handle = asp;
311 config->obj.config = config;
312 config->obj.proto = AFPPROTO_ASP;
314 memcpy(&config->obj.options, options, sizeof(struct afp_options));
315 config->optcount = refcount;
318 config->server_start = asp_start;
319 config->server_cleanup = asp_cleanup;
328 #endif /* no afp/asp */
331 static AFPConfig *DSIConfigInit(const struct afp_options *options,
332 unsigned char *refcount,
333 const dsi_proto protocol)
339 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
340 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
344 LOG(log_debug, logtype_afpd, "DSIConfigInit: hostname: %s, ip/port: %s/%s, ",
346 options->ipaddr ? options->ipaddr : "default",
347 options->port ? options->port : "548");
349 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
350 options->ipaddr, options->port,
351 options->flags & OPTION_PROXY,
352 options->server_quantum)) == NULL) {
353 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
357 dsi->dsireadbuf = options->dsireadbuf;
359 if (options->flags & OPTION_PROXY) {
360 LOG(log_note, logtype_afpd, "AFP/TCP proxy initialized for %s:%d (%s)",
361 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
363 LOG(log_note, logtype_afpd, "AFP/TCP started, advertising %s:%d (%s)",
364 getip_string((struct sockaddr *)&dsi->server), getip_port((struct sockaddr *)&dsi->server), VERSION);
368 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
369 if (!(options->flags & OPTION_NOSLP)) {
371 SLPError callbackerr;
373 unsigned int afp_port;
375 char *srvloc_hostname;
376 const char *hostname;
378 err = SLPOpen("en", SLP_FALSE, &hslp);
380 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
384 /* XXX We don't want to tack on the port number if we don't have to.
386 * Well, this seems to break MacOS < 10. If the user _really_ wants to
387 * use a non-default port, they can, but be aware, this server might
388 * not show up int the Network Browser.
390 afp_port = getip_port((struct sockaddr *)&dsi->server);
391 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
394 hostname = options->fqdn;
395 p = strchr(hostname, ':');
398 hostname = getip_string((struct sockaddr *)&dsi->server);
400 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
402 if ((p) || afp_port == 548) {
403 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s/?NAME=%s", hostname, srvloc_hostname);
406 l = snprintf(dsi->srvloc_url, sizeof(dsi->srvloc_url), "afp://%s:%d/?NAME=%s", hostname, afp_port, srvloc_hostname);
409 if (l == -1 || l >= (int)sizeof(dsi->srvloc_url)) {
410 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
411 dsi->srvloc_url[0] = '\0';
417 SLP_LIFETIME_MAXIMUM,
424 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
425 dsi->srvloc_url[0] = '\0';
429 if (callbackerr != SLP_OK) {
430 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
431 dsi->srvloc_url[0] = '\0';
435 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
436 config->server_cleanup = dsi_cleanup;
441 #endif /* USE_SRVLOC */
443 config->fd = dsi->serversock;
444 config->obj.handle = dsi;
445 dsi->AFPobj = &config->obj;
446 config->obj.config = config;
447 config->obj.proto = AFPPROTO_DSI;
449 memcpy(&config->obj.options, options, sizeof(struct afp_options));
450 /* get rid of any appletalk info. we use the fact that the DSI
451 * stuff is done after the ASP stuff. */
452 p = config->obj.options.server;
453 if (p && (q = strchr(p, ':')))
456 config->optcount = refcount;
459 config->server_start = dsi_start;
463 /* allocate server configurations. this should really store the last
464 * entry in config->last or something like that. that would make
465 * supporting multiple dsi transports easier. */
466 static AFPConfig *AFPConfigInit(struct afp_options *options,
467 const struct afp_options *defoptions)
469 AFPConfig *config = NULL, *next = NULL;
470 unsigned char *refcount;
472 if ((refcount = (unsigned char *)
473 calloc(1, sizeof(unsigned char))) == NULL) {
474 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
479 /* handle asp transports */
480 if ((options->transports & AFPTRANS_DDP) &&
481 (config = ASPConfigInit(options, refcount)))
482 config->defoptions = defoptions;
487 set_signature(options);
489 /* handle dsi transports and dsi proxies. we only proxy
490 * for DSI connections. */
492 /* this should have something like the following:
493 * for (i=mindsi; i < maxdsi; i++)
494 * if (options->transports & (1 << i) &&
495 * (next = DSIConfigInit(options, refcount, i)))
496 * next->defoptions = defoptions;
498 if ((options->transports & AFPTRANS_TCP) &&
499 (((options->flags & OPTION_PROXY) == 0) ||
500 ((options->flags & OPTION_PROXY) && config))
501 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
502 next->defoptions = defoptions;
504 /* load in all the authentication modules. we can load the same
505 things multiple times if necessary. however, loading different
506 things with the same names will cause complaints. by not loading
507 in any uams with proxies, we prevent ddp connections from succeeding.
509 auth_load(options->uampath, options->uamlist);
511 /* this should be able to accept multiple dsi transports. i think
512 * the only thing that gets affected is the net addresses. */
513 status_init(config, next, options);
515 /* attach dsi config to tail of asp config */
524 /* fill in the appropriate bits for each interface */
525 AFPConfig *configinit(struct afp_options *cmdline)
528 char buf[LINESIZE + 1], *p, have_option = 0;
530 struct afp_options options;
531 AFPConfig *config=NULL, *first = NULL;
533 /* if config file doesn't exist, load defaults */
534 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
536 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
537 cmdline->configfile);
538 return AFPConfigInit(cmdline, cmdline);
541 /* scan in the configuration file */
544 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
547 if ( len >= 2 && buf[len-2] == '\\' ) {
553 /* a little pre-processing to get rid of spaces and end-of-lines */
555 while (p && isspace(*p))
557 if (!p || (*p == '\0'))
562 memcpy(&options, cmdline, sizeof(options));
563 if (!afp_options_parseline(p, &options))
566 /* AFPConfigInit can return two linked configs due to DSI and ASP */
568 if ((first = AFPConfigInit(&options, cmdline)))
569 config = first->next ? first->next : first;
570 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
571 config = config->next->next ? config->next->next : config->next;
576 /* Parse afp_ldap.conf */
577 acl_ldap_readconfig(_PATH_ACL_LDAPCONF);
578 #endif /* HAVE_LDAP */
580 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
584 first = AFPConfigInit(cmdline, cmdline);
586 /* Now register with zeroconf, we also need the volumes for that */
587 if (first && !(first->obj.options.flags & OPTION_NOZEROCONF)) {
588 load_volumes(&first->obj);
589 zeroconf_register(first);