2 * $Id: afp_config.c,v 1.24 2009-01-16 18:45:26 morgana 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>
37 #include <atalk/util.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <arpa/inet.h>
43 #include <atalk/dsi.h>
44 #include <atalk/atp.h>
45 #include <atalk/asp.h>
46 #include <atalk/nbp.h>
47 #include <atalk/afp.h>
48 #include <atalk/compat.h>
49 #include <atalk/server_child.h>
52 #endif /* USE_SRVLOC */
55 #include "afp_config.h"
61 /* get rid of unneeded configurations. i use reference counts to deal
62 * w/ multiple configs sharing the same afp_options. oh, to dream of
63 * garbage collection ... */
64 void configfree(AFPConfig *configs, const AFPConfig *config)
68 for (p = configs; p; p = q) {
73 /* do a little reference counting */
74 if (--(*p->optcount) < 1) {
75 afp_options_free(&p->obj.options, p->defoptions);
79 switch (p->obj.proto) {
85 atp_close(((ASP) p->obj.handle)->asp_atp);
88 #endif /* no afp/asp */
99 static void SRVLOC_callback(SLPHandle hslp _U_, SLPError errcode, void *cookie) {
100 *(SLPError*)cookie = errcode;
103 static char hex[17] = "0123456789abcdef";
105 static char * srvloc_encode(const struct afp_options *options, const char *name)
107 static char buf[512];
112 char *Obj, *Type = "", *Zone = "";
115 /* Convert name to maccharset */
116 if ((size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
117 name, strlen(name), &conv_name)) )
120 /* Escape characters */
122 while (*p && i<(sizeof(buf)-4)) {
125 else if (isspace(*p)) {
131 else if ((!isascii(*p)) || *p <= 0x2f || *p == 0x3f ) {
133 buf[i++] = hex[*p >> 4];
134 buf[i++] = hex[*p++ & 15];
144 if (nbp_name(options->server, &Obj, &Type, &Zone )) {
145 LOG(log_error, logtype_afpd, "srvloc_encode: can't parse %s", options->server );
148 snprintf( buf+i, sizeof(buf)-i-1 ,"&ZONE=%s", Zone);
155 #endif /* USE_SRVLOC */
158 static void dsi_cleanup(const AFPConfig *config)
161 SLPError callbackerr;
163 DSI *dsi = (DSI *)config->obj.handle;
165 /* Do nothing if we didn't register. */
166 if (!dsi || dsi->srvloc_url[0] == '\0')
169 err = SLPOpen("en", SLP_FALSE, &hslp);
171 LOG(log_error, logtype_afpd, "dsi_cleanup: Error opening SRVLOC handle");
172 goto srvloc_dereg_err;
180 LOG(log_error, logtype_afpd, "dsi_cleanup: Error unregistering %s from SRVLOC", dsi->srvloc_url);
181 goto srvloc_dereg_err;
184 if (callbackerr != SLP_OK) {
185 LOG(log_error, logtype_afpd, "dsi_cleanup: Error in callback while trying to unregister %s from SRVLOC (%d)", dsi->srvloc_url, callbackerr);
186 goto srvloc_dereg_err;
190 dsi->srvloc_url[0] = '\0';
193 #endif /* USE_SRVLOC */
196 static void asp_cleanup(const AFPConfig *config)
198 /* we need to stop tickle handler */
200 nbp_unrgstr(config->obj.Obj, config->obj.Type, config->obj.Zone,
201 &config->obj.options.ddpaddr);
204 /* these two are almost identical. it should be possible to collapse them
205 * into one with minimal junk. */
206 static int asp_start(AFPConfig *config, AFPConfig *configs,
207 server_child *server_children)
211 if (!(asp = asp_getsession(config->obj.handle, server_children,
212 config->obj.options.tickleval))) {
213 LOG(log_error, logtype_afpd, "main: asp_getsession: %s", strerror(errno) );
214 exit( EXITERR_CLNT );
218 configfree(configs, config); /* free a bunch of stuff */
219 afp_over_asp(&config->obj);
225 #endif /* no afp/asp */
227 static int dsi_start(AFPConfig *config, AFPConfig *configs,
228 server_child *server_children)
232 if (!(dsi = dsi_getsession(config->obj.handle, server_children,
233 config->obj.options.tickleval))) {
234 LOG(log_error, logtype_afpd, "main: dsi_getsession: %s", strerror(errno) );
235 exit( EXITERR_CLNT );
240 configfree(configs, config);
241 afp_over_dsi(&config->obj); /* start a session */
249 static AFPConfig *ASPConfigInit(const struct afp_options *options,
250 unsigned char *refcount)
255 char *Obj, *Type = "AFPServer", *Zone = "*";
256 char *convname = NULL;
258 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL)
261 if ((atp = atp_open(ATADDR_ANYPORT, &options->ddpaddr)) == NULL) {
262 LOG(log_error, logtype_afpd, "main: atp_open: %s", strerror(errno) );
267 if ((asp = asp_init( atp )) == NULL) {
268 LOG(log_error, logtype_afpd, "main: asp_init: %s", strerror(errno) );
274 /* register asp server */
275 Obj = (char *) options->hostname;
276 if (options->server && (size_t)-1 ==(convert_string_allocate( options->unixcharset, options->maccharset,
277 options->server, strlen(options->server), &convname)) ) {
278 if ((convname = strdup(options->server)) == NULL ) {
279 LOG(log_error, logtype_afpd, "malloc: %s", strerror(errno) );
280 goto serv_free_return;
284 if (nbp_name(convname, &Obj, &Type, &Zone )) {
285 LOG(log_error, logtype_afpd, "main: can't parse %s", options->server );
286 goto serv_free_return;
291 /* dup Obj, Type and Zone as they get assigned to a single internal
292 * buffer by nbp_name */
293 if ((config->obj.Obj = strdup(Obj)) == NULL)
294 goto serv_free_return;
296 if ((config->obj.Type = strdup(Type)) == NULL) {
297 free(config->obj.Obj);
298 goto serv_free_return;
301 if ((config->obj.Zone = strdup(Zone)) == NULL) {
302 free(config->obj.Obj);
303 free(config->obj.Type);
304 goto serv_free_return;
307 /* make sure we're not registered */
308 nbp_unrgstr(Obj, Type, Zone, &options->ddpaddr);
309 if (nbp_rgstr( atp_sockaddr( atp ), Obj, Type, Zone ) < 0 ) {
310 LOG(log_error, logtype_afpd, "Can't register %s:%s@%s", Obj, Type, Zone );
311 free(config->obj.Obj);
312 free(config->obj.Type);
313 free(config->obj.Zone);
314 goto serv_free_return;
317 LOG(log_info, logtype_afpd, "%s:%s@%s started on %u.%u:%u (%s)", Obj, Type, Zone,
318 ntohs( atp_sockaddr( atp )->sat_addr.s_net ),
319 atp_sockaddr( atp )->sat_addr.s_node,
320 atp_sockaddr( atp )->sat_port, VERSION );
322 config->fd = atp_fileno(atp);
323 config->obj.handle = asp;
324 config->obj.config = config;
325 config->obj.proto = AFPPROTO_ASP;
327 memcpy(&config->obj.options, options, sizeof(struct afp_options));
328 config->optcount = refcount;
331 config->server_start = asp_start;
332 config->server_cleanup = asp_cleanup;
341 #endif /* no afp/asp */
344 static AFPConfig *DSIConfigInit(const struct afp_options *options,
345 unsigned char *refcount,
346 const dsi_proto protocol)
353 SLPError callbackerr;
355 struct servent *afpovertcp;
357 char *srvloc_hostname, *hostname;
358 #endif /* USE_SRVLOC */
360 if ((config = (AFPConfig *) calloc(1, sizeof(AFPConfig))) == NULL) {
361 LOG(log_error, logtype_afpd, "DSIConfigInit: malloc(config): %s", strerror(errno) );
365 if ((dsi = dsi_init(protocol, "afpd", options->hostname,
366 options->ipaddr, options->port,
367 options->flags & OPTION_PROXY,
368 options->server_quantum)) == NULL) {
369 LOG(log_error, logtype_afpd, "main: dsi_init: %s", strerror(errno) );
374 if (options->flags & OPTION_PROXY) {
375 LOG(log_info, logtype_afpd, "ASIP proxy initialized for %s:%d (%s)",
376 inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
379 LOG(log_info, logtype_afpd, "ASIP started on %s:%d(%d) (%s)",
380 inet_ntoa(dsi->server.sin_addr), ntohs(dsi->server.sin_port),
381 dsi->serversock, VERSION);
385 dsi->srvloc_url[0] = '\0'; /* Mark that we haven't registered. */
386 if (!(options->flags & OPTION_NOSLP)) {
387 err = SLPOpen("en", SLP_FALSE, &hslp);
389 LOG(log_error, logtype_afpd, "DSIConfigInit: Error opening SRVLOC handle");
393 /* XXX We don't want to tack on the port number if we don't have to.
395 * Well, this seems to break MacOS < 10. If the user _really_ wants to
396 * use a non-default port, they can, but be aware, this server might
397 * not show up int the Network Browser.
399 afpovertcp = getservbyname("afpovertcp", "tcp");
400 if (afpovertcp != NULL) {
401 afp_port = afpovertcp->s_port;
403 /* If specified use the FQDN to register with srvloc, otherwise use IP. */
406 hostname = options->fqdn;
407 p = strchr(hostname, ':');
410 hostname = inet_ntoa(dsi->server.sin_addr);
411 srvloc_hostname = srvloc_encode(options, (options->server ? options->server : options->hostname));
413 if (strlen(srvloc_hostname) > (sizeof(dsi->srvloc_url) - strlen(hostname) - 21)) {
414 LOG(log_error, logtype_afpd, "DSIConfigInit: Hostname is too long for SRVLOC");
415 dsi->srvloc_url[0] = '\0';
418 if ((p) || dsi->server.sin_port == afp_port) {
419 sprintf(dsi->srvloc_url, "afp://%s/?NAME=%s", hostname, srvloc_hostname);
422 sprintf(dsi->srvloc_url, "afp://%s:%d/?NAME=%s", hostname, ntohs(dsi->server.sin_port), srvloc_hostname);
427 SLP_LIFETIME_MAXIMUM,
434 LOG(log_error, logtype_afpd, "DSIConfigInit: Error registering %s with SRVLOC", dsi->srvloc_url);
435 dsi->srvloc_url[0] = '\0';
439 if (callbackerr != SLP_OK) {
440 LOG(log_error, logtype_afpd, "DSIConfigInit: Error in callback trying to register %s with SRVLOC", dsi->srvloc_url);
441 dsi->srvloc_url[0] = '\0';
445 LOG(log_info, logtype_afpd, "Sucessfully registered %s with SRVLOC", dsi->srvloc_url);
450 #endif /* USE_SRVLOC */
453 config->fd = dsi->serversock;
454 config->obj.handle = dsi;
455 config->obj.config = config;
456 config->obj.proto = AFPPROTO_DSI;
458 memcpy(&config->obj.options, options, sizeof(struct afp_options));
459 /* get rid of any appletalk info. we use the fact that the DSI
460 * stuff is done after the ASP stuff. */
461 p = config->obj.options.server;
462 if (p && (q = strchr(p, ':')))
465 config->optcount = refcount;
468 config->server_start = dsi_start;
470 config->server_cleanup = dsi_cleanup;
475 /* allocate server configurations. this should really store the last
476 * entry in config->last or something like that. that would make
477 * supporting multiple dsi transports easier. */
478 static AFPConfig *AFPConfigInit(const struct afp_options *options,
479 const struct afp_options *defoptions)
481 AFPConfig *config = NULL, *next = NULL;
482 unsigned char *refcount;
484 if ((refcount = (unsigned char *)
485 calloc(1, sizeof(unsigned char))) == NULL) {
486 LOG(log_error, logtype_afpd, "AFPConfigInit: calloc(refcount): %s", strerror(errno) );
491 /* handle asp transports */
492 if ((options->transports & AFPTRANS_DDP) &&
493 (config = ASPConfigInit(options, refcount)))
494 config->defoptions = defoptions;
497 /* handle dsi transports and dsi proxies. we only proxy
498 * for DSI connections. */
500 /* this should have something like the following:
501 * for (i=mindsi; i < maxdsi; i++)
502 * if (options->transports & (1 << i) &&
503 * (next = DSIConfigInit(options, refcount, i)))
504 * next->defoptions = defoptions;
506 if ((options->transports & AFPTRANS_TCP) &&
507 (((options->flags & OPTION_PROXY) == 0) ||
508 ((options->flags & OPTION_PROXY) && config))
509 && (next = DSIConfigInit(options, refcount, DSI_TCPIP)))
510 next->defoptions = defoptions;
512 /* load in all the authentication modules. we can load the same
513 things multiple times if necessary. however, loading different
514 things with the same names will cause complaints. by not loading
515 in any uams with proxies, we prevent ddp connections from succeeding.
517 auth_load(options->uampath, options->uamlist);
519 /* this should be able to accept multiple dsi transports. i think
520 * the only thing that gets affected is the net addresses. */
521 status_init(config, next, options);
523 /* attach dsi config to tail of asp config */
532 /* fill in the appropriate bits for each interface */
533 AFPConfig *configinit(struct afp_options *cmdline)
536 char buf[LINESIZE + 1], *p, have_option = 0;
538 struct afp_options options;
539 AFPConfig *config=NULL, *first = NULL;
542 /* if config file doesn't exist, load defaults */
543 if ((fp = fopen(cmdline->configfile, "r")) == NULL)
545 LOG(log_debug, logtype_afpd, "ConfigFile %s not found, assuming defaults",
546 cmdline->configfile);
547 return AFPConfigInit(cmdline, cmdline);
550 LOG(log_debug, logtype_afpd, "Loading ConfigFile");
552 /* scan in the configuration file */
555 if (!fgets(&buf[len], LINESIZE - len, fp) || buf[len] == '#')
558 if ( buf[len-2] == '\\' ) {
564 /* a little pre-processing to get rid of spaces and end-of-lines */
566 while (p && isspace(*p))
568 if (!p || (*p == '\0'))
573 memcpy(&options, cmdline, sizeof(options));
574 if (!afp_options_parseline(p, &options))
577 /* this should really get a head and a tail to simplify things. */
579 if ((first = AFPConfigInit(&options, cmdline)))
580 config = first->next ? first->next : first;
581 } else if ((config->next = AFPConfigInit(&options, cmdline))) {
582 config = config->next->next ? config->next->next : config->next;
586 LOG(log_debug, logtype_afpd, "Finished parsing Config File");
590 return AFPConfigInit(cmdline, cmdline);