diff dbutil.c @ 62:20563735e8b5

just checkpointing
author Matt Johnston <matt@ucc.asn.au>
date Tue, 10 Aug 2004 17:09:52 +0000
parents 095d689fed16
children dcc43965928f
line wrap: on
line diff
--- a/dbutil.c	Mon Aug 09 08:18:37 2004 +0000
+++ b/dbutil.c	Tue Aug 10 17:09:52 2004 +0000
@@ -113,8 +113,109 @@
 }
 #endif /* DEBUG_TRACE */
 
+/* Listen on address:port. Unless address is NULL, in which case listen on
+ * everything (ie 0.0.0.0, or ::1 - note that this is IPv? agnostic. Linux is
+ * broken with respect to listening to v6 or v4, so the addresses you get when
+ * people connect will be wrong. It doesn't break things, just looks quite
+ * ugly. Returns the number of sockets bound on success, or -1 on failure. On
+ * failure, if errstring wasn't NULL, it'll be a newly malloced error
+ * string.*/
+int dropbear_listen(const char* address, const char* port,
+		int *socks, unsigned int sockcount, char **errstring, int *maxfd) {
+
+	struct addrinfo hints, *res, *res0;
+	int err;
+	unsigned int nsock;
+	struct linger linger;
+	int val;
+	int sock;
+
+	TRACE(("enter dropbear_listen"));
+	
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_PASSIVE;
+	err = getaddrinfo(address, port, &hints, &res0);
+
+	if (err) {
+		if (errstring != NULL && *errstring == NULL) {
+			int len;
+			len = 20 + strlen(gai_strerror(err));
+			*errstring = (char*)m_malloc(len);
+			snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
+		}
+		TRACE(("leave dropbear_listen: failed resolving"));
+		return -1;
+	}
+
+
+	nsock = 0;
+	for (res = res0; res != NULL && nsock < sockcount;
+			res = res->ai_next) {
+
+		/* Get a socket */
+		socks[nsock] = socket(res->ai_family, res->ai_socktype,
+				res->ai_protocol);
+
+		sock = socks[nsock]; /* For clarity */
+
+		if (sock < 0) {
+			err = errno;
+			TRACE(("socket() failed"));
+			continue;
+		}
+
+		/* Various useful socket options */
+		val = 1;
+		/* set to reuse, quick timeout */
+		setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void*) &val, sizeof(val));
+		linger.l_onoff = 1;
+		linger.l_linger = 5;
+		setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger));
+
+		/* disable nagle */
+		setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
+
+		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+			err = errno;
+			close(sock);
+			TRACE(("bind() failed"));
+			continue;
+		}
+
+		if (listen(sock, 20) < 0) {
+			err = errno;
+			close(sock);
+			TRACE(("listen() failed"));
+			continue;
+		}
+
+		*maxfd = MAX(*maxfd, sock);
+
+		nsock++;
+	}
+
+	if (nsock == 0) {
+		if (errstring != NULL && *errstring == NULL) {
+			int len;
+			len = 20 + strlen(strerror(err));
+			*errstring = (char*)m_malloc(len);
+			snprintf(*errstring, len, "Error connecting: %s", strerror(err));
+			TRACE(("leave dropbear_listen: failure, %s", strerror(err)));
+			return -1;
+		}
+	}
+
+	TRACE(("leave dropbear_listen: success, %d socks bound", nsock));
+	return nsock;
+}
+
 /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
- * return immediately if nonblocking is set */
+ * return immediately if nonblocking is set. On failure, if errstring
+ * wasn't null, it will be a newly malloced error message */
+
+/* TODO: maxfd */
 int connect_remote(const char* remotehost, const char* remoteport,
 		int nonblocking, char ** errstring) {
 
@@ -197,58 +298,70 @@
 	}
 
 	freeaddrinfo(res0);
+	if (sock > 0 && errstring != NULL && *errstring != NULL) {
+		m_free(*errstring);
+	}
 
-	TRACE(("leave connect_remote: sock %d", sock));
+	TRACE(("leave connect_remote: sock %d\n", sock));
 	return sock;
 }
 
 /* Return a string representation of the socket address passed. The return
  * value is allocated with malloc() */
-unsigned char * getaddrstring(struct sockaddr * addr) {
+unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
 
-	char *retstring;
+	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+	char *retstring = NULL;
+	int ret;
+	unsigned int len;
 
-	/* space for "255.255.255.255:65535\0" = 22 */
-	retstring = m_malloc(22);
+	len = sizeof(struct sockaddr_storage);
+
+	ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), 
+			sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST);
 
-	switch (addr->sa_family) {
-		case PF_INET: 
-			snprintf(retstring, 22, "%s:%hu",
-					inet_ntoa(((struct sockaddr_in *)addr)->sin_addr),
-					((struct sockaddr_in *)addr)->sin_port);
-			break;
+	if (ret != 0) {
+		/* This is a fairly bad failure - it'll fallback to IP if it
+		 * just can't resolve */
+		dropbear_exit("failed lookup (%d, %d)", ret, errno);
+	}
 
-		default:
-			/* XXX ipv6 */
-			strcpy(retstring, "Bad protocol");
+	if (withport) {
+		len = strlen(hbuf) + 2 + strlen(sbuf);
+		retstring = (char*)m_malloc(len);
+		snprintf(retstring, len, "%s:%s", hbuf, sbuf);
+	} else {
+		retstring = m_strdup(hbuf);
+	}
 
-	}
 	return retstring;
 
 }
 
 /* Get the hostname corresponding to the address addr. On failure, the IP
  * address is returned. The return value is allocated with strdup() */
-char* getaddrhostname(struct sockaddr * addr) {
+char* getaddrhostname(struct sockaddr_storage * addr) {
 
-	struct hostent *host = NULL;
-	char * retstring;
+	char hbuf[NI_MAXHOST];
+	char sbuf[NI_MAXSERV];
+	int ret;
+	unsigned int len;
+
+	len = sizeof(struct sockaddr_storage);
 
-#ifdef DO_HOST_LOOKUP
-	host = gethostbyaddr((char*)&((struct sockaddr_in*)addr)->sin_addr,
-			sizeof(struct in_addr), AF_INET);
-#endif
-	
-	if (host == NULL) {
-		/* return the address */
-		retstring = inet_ntoa(((struct sockaddr_in *)addr)->sin_addr);
-	} else {
-		/* return the hostname */
-		retstring = host->h_name;
+	ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
+			sbuf, sizeof(sbuf), NI_NUMERICSERV);
+
+	if (ret != 0) {
+		/* On some systems (Darwin does it) we get EINTR from getnameinfo
+		 * somehow. Eew. So we'll just return the IP, since that doesn't seem
+		 * to exhibit that behaviour. */
+		return getaddrstring(addr, 0);
 	}
 
-	return m_strdup(retstring);
+	return m_strdup(hbuf);
 }
+
 #ifdef DEBUG_TRACE
 void printhex(unsigned char* buf, int len) {