#include "config.h"
#endif /* HAVE_CONFIG_H */
-#define USE_TCP_NODELAY
-
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <atalk/dsi.h>
#include <atalk/compat.h>
#include <atalk/util.h>
-
-#include "dsi_private.h"
+#include <atalk/errchk.h>
#define min(a,b) ((a) < (b) ? (a) : (b))
exit(EXITERR_CLNT);
}
+/*!
+ * Allocate DSI read buffer and read-ahead buffer
+ */
+static void dsi_init_buffer(DSI *dsi)
+{
+ if ((dsi->commands = malloc(dsi->server_quantum)) == NULL) {
+ LOG(log_error, logtype_dsi, "dsi_init_buffer: OOM");
+ AFP_PANIC("OOM in dsi_init_buffer");
+ }
+
+ /* dsi_peek() read ahead buffer, default is 12 * 300k = 3,6 MB (Apr 2011) */
+ if ((dsi->buffer = malloc(dsi->dsireadbuf * dsi->server_quantum)) == NULL) {
+ LOG(log_error, logtype_dsi, "dsi_init_buffer: OOM");
+ AFP_PANIC("OOM in dsi_init_buffer");
+ }
+ dsi->start = dsi->buffer;
+ dsi->eof = dsi->buffer;
+ dsi->end = dsi->buffer + (dsi->dsireadbuf * dsi->server_quantum);
+}
+
+/*!
+ * Free any allocated ressources of the master afpd DSI objects and close server socket
+ */
+void dsi_free(DSI *dsi)
+{
+ close(dsi->serversock);
+ dsi->serversock = -1;
+
+ free(dsi->commands);
+ dsi->commands = NULL;
+
+ free(dsi->buffer);
+ dsi->buffer = NULL;
+
+#ifdef USE_ZEROCONF
+ free(dsi->bonjourname);
+ dsi->bonjourname = NULL;
+#endif
+}
+
static struct itimerval itimer;
/* accept the socket and do a little sanity checking */
-static int dsi_tcp_open(DSI *dsi)
+static pid_t dsi_tcp_open(DSI *dsi)
{
pid_t pid;
SOCKLEN_T len;
#ifdef TCPWRAP
{
struct request_info req;
- request_init(&req, RQ_DAEMON, dsi->program, RQ_FILE, dsi->socket, NULL);
+ request_init(&req, RQ_DAEMON, "afpd", RQ_FILE, dsi->socket, NULL);
fromhost(&req);
if (!hosts_access(&req)) {
LOG(deny_severity, logtype_dsi, "refused connect from %s", eval_client(&req));
if (0 == (pid = fork()) ) { /* child */
static struct itimerval timer = {{0, 0}, {DSI_TCPTIMEOUT, 0}};
struct sigaction newact, oldact;
- u_int8_t block[DSI_BLOCKSIZ];
+ uint8_t block[DSI_BLOCKSIZ];
size_t stored;
- /* Immediateyl mark globally that we're a child now */
- parent_or_child = 1;
-
/* reset signals */
server_reset_signal();
}
#endif
+ dsi_init_buffer(dsi);
+
/* read in commands. this is similar to dsi_receive except
* for the fact that we do some sanity checking to prevent
* delinquent connections from causing mischief. */
len = dsi_stream_read(dsi, block, 2);
if (!len ) {
/* connection already closed, don't log it (normal OSX 10.3 behaviour) */
- exit(EXITERR_CLNT);
+ exit(EXITERR_CLOSED);
}
if (len < 2 || (block[0] > DSIFL_MAX) || (block[1] > DSIFUNC_MAX)) {
LOG(log_error, logtype_dsi, "dsi_tcp_open: invalid header");
dsi->header.dsi_command = block[1];
memcpy(&dsi->header.dsi_requestID, block + 2,
sizeof(dsi->header.dsi_requestID));
- memcpy(&dsi->header.dsi_code, block + 4, sizeof(dsi->header.dsi_code));
+ memcpy(&dsi->header.dsi_data.dsi_code, block + 4, sizeof(dsi->header.dsi_data.dsi_code));
memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
memcpy(&dsi->header.dsi_reserved, block + 12,
sizeof(dsi->header.dsi_reserved));
dsi->clientID = ntohs(dsi->header.dsi_requestID);
/* make sure we don't over-write our buffers. */
- dsi->cmdlen = min(ntohl(dsi->header.dsi_len), DSI_CMDSIZ);
+ dsi->cmdlen = min(ntohl(dsi->header.dsi_len), dsi->server_quantum);
stored = 0;
while (stored < dsi->cmdlen) {
getip_string((struct sockaddr *)&dsi->server), port, ifr.ifr_name);
goto iflist_done;
}
- LOG(log_info, logtype_dsi, "dsi_tcp (Chooser will not select afp/tcp) "
- "Check to make sure %s is in /etc/hosts and the correct domain is in "
- "/etc/resolv.conf: %s", hostname, strerror(errno));
+
+ LOG(log_note, logtype_dsi, "dsi_tcp: couldn't find network interface with IP address to advertice, "
+ "check to make sure \"%s\" is in /etc/hosts or can be resolved with DNS, or "
+ "add a netinterface that is not a loopback or point-2-point type", hostname);
iflist_done:
close(fd);
#define AI_NUMERICSERV 0
#endif
-/* this needs to accept passed in addresses */
-int dsi_tcp_init(DSI *dsi, const char *hostname, const char *address,
- const char *port, const int proxy)
+/*!
+ * Initialize DSI over TCP
+ *
+ * @param dsi (rw) DSI handle
+ * @param hostname (r) pointer to hostname string
+ * @param inaddress (r) Optional IPv4 or IPv6 address with an optional port, may be NULL
+ * @param inport (r) pointer to port string
+ *
+ * Creates listening AFP/DSI socket. If the parameter inaddress is NULL, then we listen
+ * on the wildcard address, ie on all interfaces. That should mean listening on the IPv6
+ * address "::" on IPv4/IPv6 dual stack kernels, accepting both v4 and v6 requests.
+ *
+ * If the parameter inaddress is not NULL, then we only listen on the given address.
+ * The parameter may contain a port number using the URL format for address and port:
+ *
+ * IPv4, IPv4:port, IPv6, [IPv6], [IPv6]:port
+ *
+ * Parameter inport must be a valid pointer to a port string and is used if the inaddress
+ * parameter doesn't contain a port.
+ *
+ * @returns 0 on success, -1 on failure
+ */
+int dsi_tcp_init(DSI *dsi, const char *hostname, const char *inaddress, const char *inport)
{
- int ret;
- int flag;
+ EC_INIT;
+ int flag, err;
+ char *address = NULL, *port = NULL;
struct addrinfo hints, *servinfo, *p;
- dsi->protocol = DSI_TCPIP;
+ /* inaddress may be NULL */
+ AFP_ASSERT(dsi && hostname && inport);
+
+ if (inaddress)
+ /* Check whether address is of the from IP:PORT and split */
+ EC_ZERO( tokenize_ip_port(inaddress, &address, &port) );
+
+ if (port == NULL)
+ /* inport is supposed to always contain a valid port string */
+ EC_NULL( port = strdup(inport) );
/* Prepare hint for getaddrinfo */
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
#endif
}
- if ((ret = getaddrinfo(address ? address : NULL, port ? port : "548", &hints, &servinfo)) != 0) {
- LOG(log_error, logtype_dsi, "dsi_tcp_init: getaddrinfo: %s\n", gai_strerror(ret));
- return 0;
+ if ((ret = getaddrinfo(address, port, &hints, &servinfo)) != 0) {
+ LOG(log_error, logtype_dsi, "dsi_tcp_init(%s): getaddrinfo: %s\n", address ? address : "*", gai_strerror(ret));
+ EC_FAIL;
}
- /* create a socket */
- if (proxy)
- dsi->serversock = -1;
- else {
- /* loop through all the results and bind to the first we can */
- for (p = servinfo; p != NULL; p = p->ai_next) {
- if ((dsi->serversock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
- LOG(log_info, logtype_dsi, "dsi_tcp_init: socket: %s", strerror(errno));
- continue;
- }
+ /* loop through all the results and bind to the first we can */
+ for (p = servinfo; p != NULL; p = p->ai_next) {
+ if ((dsi->serversock = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+ LOG(log_info, logtype_dsi, "dsi_tcp_init: socket: %s", strerror(errno));
+ continue;
+ }
- /*
- * Set some socket options:
- * SO_REUSEADDR deals w/ quick close/opens
- * TCP_NODELAY diables Nagle
- */
+ /*
+ * Set some socket options:
+ * SO_REUSEADDR deals w/ quick close/opens
+ * TCP_NODELAY diables Nagle
+ */
#ifdef SO_REUSEADDR
- flag = 1;
- setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
+ flag = 1;
+ setsockopt(dsi->serversock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag));
#endif
#if defined(FREEBSD) && defined(IPV6_BINDV6ONLY)
- int on = 0;
- setsockopt(dsi->serversock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (on));
+ int on = 0;
+ setsockopt(dsi->serversock, IPPROTO_IPV6, IPV6_BINDV6ONLY, (char *)&on, sizeof (on));
#endif
-#ifdef USE_TCP_NODELAY
#ifndef SOL_TCP
#define SOL_TCP IPPROTO_TCP
#endif
- flag = 1;
- setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
-#endif /* USE_TCP_NODELAY */
-
- if (bind(dsi->serversock, p->ai_addr, p->ai_addrlen) == -1) {
- close(dsi->serversock);
- LOG(log_info, logtype_dsi, "dsi_tcp_init: bind: %s\n", strerror(errno));
- continue;
- }
-
- if (listen(dsi->serversock, DSI_TCPMAXPEND) < 0) {
- close(dsi->serversock);
- LOG(log_info, logtype_dsi, "dsi_tcp_init: listen: %s\n", strerror(errno));
- continue;
- }
+ flag = 1;
+ setsockopt(dsi->serversock, SOL_TCP, TCP_NODELAY, &flag, sizeof(flag));
- break;
+ if (bind(dsi->serversock, p->ai_addr, p->ai_addrlen) == -1) {
+ close(dsi->serversock);
+ LOG(log_info, logtype_dsi, "dsi_tcp_init: bind: %s\n", strerror(errno));
+ continue;
}
- if (p == NULL) {
- LOG(log_error, logtype_dsi, "dsi_tcp_init: no suitable network config for TCP socket");
- freeaddrinfo(servinfo);
- return 0;
+ if (listen(dsi->serversock, DSI_TCPMAXPEND) < 0) {
+ close(dsi->serversock);
+ LOG(log_info, logtype_dsi, "dsi_tcp_init: listen: %s\n", strerror(errno));
+ continue;
}
+
+ break;
+ }
- /* Copy struct sockaddr to struct sockaddr_storage */
- memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
+ if (p == NULL) {
+ LOG(log_error, logtype_dsi, "dsi_tcp_init: no suitable network config for TCP socket");
freeaddrinfo(servinfo);
- } /* if (proxy) */
+ EC_FAIL;
+ }
+
+ /* Copy struct sockaddr to struct sockaddr_storage */
+ memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
+ freeaddrinfo(servinfo);
/* Point protocol specific functions to tcp versions */
dsi->proto_open = dsi_tcp_open;
if (address) {
/* address is a parameter, use it 'as is' */
- return 1;
+ goto EC_CLEANUP;
}
/* Prepare hint for getaddrinfo */
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
- if ((ret = getaddrinfo(hostname, port ? port : "548", &hints, &servinfo)) != 0) {
- LOG(log_info, logtype_dsi, "dsi_tcp_init: getaddrinfo '%s': %s\n", hostname, gai_strerror(ret));
+ if ((err = getaddrinfo(hostname, port, &hints, &servinfo)) != 0) {
+ LOG(log_info, logtype_dsi, "dsi_tcp_init: getaddrinfo '%s': %s\n", hostname, gai_strerror(err));
goto interfaces;
}
/* Store found address in dsi->server */
memcpy(&dsi->server, p->ai_addr, p->ai_addrlen);
freeaddrinfo(servinfo);
- return 1;
+ goto EC_CLEANUP;
}
LOG(log_info, logtype_dsi, "dsi_tcp: hostname '%s' resolves to loopback address", hostname);
freeaddrinfo(servinfo);
interfaces:
guess_interface(dsi, hostname, port ? port : "548");
- return 1;
+
+EC_CLEANUP:
+ if (address)
+ free(address);
+ if (port)
+ free(port);
+ EC_EXIT;
}