Mercurial > dropbear
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) {