2 * $Id: afp_config.c,v 1.22.6.3 2004-01-23 14:00:15 bfernhomberg Exp $
4 * Copyright (c) 1997 Adrian Sun (asun@zoology.washington.edu)
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
20 #else /* STDC_HEADERS */
24 #endif /* HAVE_STRCHR */
25 char *strchr (), *strrchr ();
27 #define memcpy(d,s,n) bcopy ((s), (d), (n))
28 #define memmove(d,s,n) bcopy ((s), (d), (n))
29 #endif /* ! HAVE_MEMCPY */
30 #endif /* STDC_HEADERS */
34 #endif /* HAVE_UNISTD_H */
36 #include <atalk/logger.h>
38 #include <sys/socket.h>
39 #include <netinet/in.h>
40 #include <arpa/inet.h>
42 #include <atalk/dsi.h>
43 #include <atalk/atp.h>
44 #include <atalk/asp.h>
45 #include <atalk/nbp.h>
46 #include <atalk/afp.h>
47 #include <atalk/compat.h>
48 #include <atalk/server_child.h>
51 #endif /* USE_SRVLOC */
54 #include "afp_config.h"
60 /* get rid of unneeded configurations. i use reference counts to deal
61 * w/ multiple configs sharing the same afp_options. oh, to dream of
62 * garbage collection ... */
63 void configfree(AFPConfig *configs, const AFPConfig *config)
67 for (p = configs; p; p = q) {
72 /* do a little reference counting */
73 if (--(*p->optcount) < 1) {
74 afp_options_free(&p->obj.options, p->defoptions);
78 switch (p->obj.proto) {
84 atp_close(((ASP) p->obj.handle)->asp_atp);
87 #endif /* no afp/asp */
98 static void SRVLOC_callback(SLPHandle hslp, SLPError errcode, void *cookie) {
99 *(SLPError*)cookie = errcode;
102 static char hex[17] = "0123456789abcdef";
104 static char * srvloc_encode(const struct afp_options *options, const char *name)
106 static char buf[512];
111 char *Obj, *Type = "", *Zone = "";
114 /* Convert name to maccharset */
115 if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
116 name, strlen(name), &conv_name)) )
119 /* Escape characters */
121 while (*p && i<(sizeof(buf)-4)) {
124 else if (isspace(*p)) {
130 else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
132 buf[i++] = hex[*p >> 4];
133 buf[i++] = hex[*p++ & 15];
143 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
144 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
147 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
154 #endif /* USE_SRVLOC */
157 static void dsi_cleanup(const AFPConfig *config)
160 SLPError callbackerr;
162 DSI *dsi = (DSI *)config->obj.handle;
164 /* Do nothing if we didn't register. */
165 if (!dsi || dsi->srvloc_url[0] == '\0')
168 err = SLPOpen("en", SLP_FALSE, &hslp);
170 LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
171 goto srvloc_dereg_err;
179 LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
180 goto srvloc_dereg_err;
183 if (callbackerr != SLP_OK) {
184 LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
185 goto srvloc_dereg_err;
189 dsi->srvloc_url[0] = '\0';
192 #endif /* USE_SRVLOC */
195 static void asp_cleanup(const AFPConfig *config)
197 nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
198 &config->obj.options.ddpaddr);
201 /* these two are almost identical. it should be possible to collapse them
202 * into one with minimal junk. */
203 static int asp_start(AFPConfig *config, AFPConfig *configs,
204 server_child *server_children)
208 if (!(asp = asp_getsession(config->obj.handle, server_children,
209 config->obj.options.tickleval))) {
210 LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
215 configfree(configs, config); /* free a bunch of stuff */
216 afp_over_asp(&config->obj);
222 #endif /* no afp/asp */
224 static int dsi_start(AFPConfig *config, AFPConfig *configs,
225 server_child *server_children)
229 if (!(dsi = dsi_getsession(config->obj.handle, server_children,
230 config->obj.options.tickleval))) {
231 LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
237 configfree(configs, config);
238 afp_over_dsi(&config->obj); /* start a session */
246 static AFPConfig *ASPConfigInit(const struct afp_options *options,
247 unsigned char *refcount)
252 char *Obj, *Type = "AFPServer", *Zone = "*";
254 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
257 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
258 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
263 if ((asp = asp_init( atp )) == NULL) {
264 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
270 /* register asp server */
271 Obj = (char *) options->hostname;
272 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
273 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
274 goto serv_free_return;
277 /* dup Obj, Type and Zone as they get assigned to a single internal
278 * buffer by nbp_name */
279 if ((config->obj.Obj = strdup(Obj)) == NULL)
280 goto serv_free_return;
282 if ((config->obj.Type = strdup(Type)) == NULL) {
283 free(config->obj.Obj);
284 goto serv_free_return;
287 if ((config->obj.Zone = strdup(Zone)) == NULL) {
288 free(config->obj.Obj);
289 free(config->obj.Type);
290 goto serv_free_return;
293 /* make sure we're not registered */
294 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
295 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
296 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
297 free(config->obj.Obj);
298 free(config->obj.Type);
299 free(config->obj.Zone);
300 goto serv_free_return;
303 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
304 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
305 atp_sockaddr( atp )->sat_addr.s_node,
306 atp_sockaddr( atp )->sat_port, VERSION );
308 config->fd = atp_fileno(atp);
309 config->obj.handle = asp;
310 config->obj.config = config;
311 config->obj.proto = AFPPROTO_ASP;
313 memcpy(&config->obj.options, options, sizeof(struct afp_options));
314 config->optcount = refcount;
317 config->server_start = asp_start;
318 config->server_cleanup = asp_cleanup;
327 #endif /* no afp/asp */
330 static AFPConfig *DSIConfigInit(const struct afp_options *options,
331 unsigned char *refcount,
332 const dsi_proto protocol)
339 SLPError callbackerr;
341 struct servent *afpovertcp;
343 char *srvloc_hostname, *hostname;
344 #endif /* USE_SRVLOC */
346 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
347 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
351 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
352 options->ipaddr, options->port,
353 options->flags & OPTION_PROXY,
354 options->server_quantum)) == NULL) {
355 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
360 if (options->flags & OPTION_PROXY) {
361 LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
362 inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
365 LOG(log_info, logtype_afpd, "ASIP started on %s:%d(%d) (%s)",
366 inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
367 dsi->serversock, VERSION);
371 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
372 if (!(options->flags & OPTION_NOSLP)) {
373 err = SLPOpen("en", SLP_FALSE, &hslp);
375 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
379 /* XXX We don't want to tack on the port number if we don't have to.
381 * Well, this seems to break MacOS < 10. If the user _really_ wants to
382 * use a non-default port, they can, but be aware, this server might
383 * not show up int the Network Browser.
385 afpovertcp = getservbyname("afpovertcp", "tcp");
386 if (afpovertcp != NULL) {
387 afp_port = afpovertcp->s_port;
389 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
392 hostname = strdup(options->fqdn);
393 p = strchr(hostname, ':');
396 hostname = inet_ntoa(dsi->server.sin_addr);
397 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
399 if (strlen(srvloc_hostname) > (sizeof(dsi->srvloc_url) - strlen(hostname) - 21)) {
400 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
401 dsi->srvloc_url[0] = '\0';
404 if ((p) || dsi->server.sin_port == afp_port) {
405 sprintf(dsi->srvloc_url, "afp://%s/?NAME=%s", hostname, srvloc_hostname);
408 sprintf(dsi->srvloc_url, "afp://%s:%d/?NAME=%s", hostname, ntohs(dsi->server.sin_port), srvloc_hostname);
413 SLP_LIFETIME_MAXIMUM,
420 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
421 dsi->srvloc_url[0] = '\0';
425 if (callbackerr != SLP_OK) {
426 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
427 dsi->srvloc_url[0] = '\0';
431 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
436 #endif /* USE_SRVLOC */
439 config->fd = dsi->serversock;
440 config->obj.handle = dsi;
441 config->obj.config = config;
442 config->obj.proto = AFPPROTO_DSI;
444 memcpy(&config->obj.options, options, sizeof(struct afp_options));
445 /* get rid of any appletalk info. we use the fact that the DSI
446 * stuff is done after the ASP stuff. */
447 p = config->obj.options.server;
448 if (p && (q = strchr(p, ':')))
451 config->optcount = refcount;
454 config->server_start = dsi_start;
456 config->server_cleanup = dsi_cleanup;
461 /* allocate server configurations. this should really store the last
462 * entry in config->last or something like that. that would make
463 * supporting multiple dsi transports easier. */
464 static AFPConfig *AFPConfigInit(const struct afp_options *options,
465 const struct afp_options *defoptions)
467 AFPConfig *config = NULL, *next = NULL;
468 unsigned char *refcount;
470 if ((refcount = (unsigned char *)
471 calloc(1, sizeof(unsigned char))) == NULL) {
472 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
477 /* handle asp transports */
478 if ((options->transports & AFPTRANS_DDP) &&
479 (config = ASPConfigInit(options, refcount)))
480 config->defoptions = defoptions;
483 /* handle dsi transports and dsi proxies. we only proxy
484 * for DSI connections. */
486 /* this should have something like the following:
487 * for (i=mindsi; i < maxdsi; i++)
488 * if (options->transports & (1 << i) &&
489 * (next = DSIConfigInit(options, refcount, i)))
490 * next->defoptions = defoptions;
492 if ((options->transports & AFPTRANS_TCP) &&
493 (((options->flags & OPTION_PROXY) == 0) ||
494 ((options->flags & OPTION_PROXY) && config))
495 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
496 next->defoptions = defoptions;
498 /* load in all the authentication modules. we can load the same
499 things multiple times if necessary. however, loading different
500 things with the same names will cause complaints. by not loading
501 in any uams with proxies, we prevent ddp connections from succeeding.
503 auth_load(options->uampath, options->uamlist);
505 /* this should be able to accept multiple dsi transports. i think
506 * the only thing that gets affected is the net addresses. */
507 status_init(config, next, options);
509 /* attach dsi config to tail of asp config */
518 /* fill in the appropriate bits for each interface */
519 AFPConfig *configinit(struct afp_options *cmdline)
522 char buf[LINESIZE + 1], *p, have_option = 0;
523 struct afp_options options;
524 AFPConfig *config, *first = NULL;
527 /* if config file doesn't exist, load defaults */
528 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
530 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
531 cmdline->configfile);
532 return AFPConfigInit(cmdline, cmdline);
535 LOG(log_debug, logtype_afpd, "Loading ConfigFile");
537 /* scan in the configuration file */
539 if (!fgets(buf, sizeof(buf), fp) || buf[0] == '#')
542 /* a little pre-processing to get rid of spaces and end-of-lines */
544 while (p && isspace(*p))
546 if (!p || (*p == '\0'))
551 memcpy(&options, cmdline, sizeof(options));
552 if (!afp_options_parseline(p, &options))
555 /* this should really get a head and a tail to simplify things. */
557 if ((first = AFPConfigInit(&options, cmdline)))
558 config = first->next ? first->next : first;
559 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
560 config = config->next->next ? config->next->next : config->next;
564 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
568 return AFPConfigInit(cmdline, cmdline);